با مطالعه و مرور جلسات گذشته مباحثی چون کپسولهسازی، کامپوننتها، دستورها و راههای برقراری ارتباط و ارسال داده بین کامپوننتها، برای شما روشن شد. حال در این جلسه قصد داریم مبحث لوکال رفرنس ها در قالبهای HTML هر کامپوننت که در فایلی با پسوند component.html وجود دارند، بررسی کرده و آنها را به دقت مورد تحلیل قرار دهیم. با ما همراه باشید.
یکی از ویژگیهای مثبتی که به هنگام استفاده از انگولار باید به آن توجه کنید وجود روشی برای ارجاع دادن به یک قالب است که در دستور شرطی ngIf آن را مطرح کردیم.
روش ارجاع دادن درون یک قالب بدین صورت است که شما بخشی از یک قالب را که شامل یک سری اطلاعات است با یک هشتگ (#) مشخص کرده و سپس نامی برای آن بر میگزینیم.
در این حالت هنگامیکه از این نام استفاده کنیم علاوه بر اینکه ساختار HTML آن قالب در اختیار ما میباشد بلکه میتوانیم به آنها درون کامپوننت خود دسترسی داشته باشیم.
برای تفهیم این موضوع به مثال زیر توجه کنید.
فرض کنید یک فرم شامل label و input در اختیار داریم. حال روی این فرم قبلا برای تبادل داده به صورت دو طرفه قبلا از دستور ngModel استفاده میکردیم.
بنابراین فرم به صورت زیر تعریف میشود:
<div class="row"> <div class="col-xs-12"> <label for="name">نام سرور</label> <input type="text" id="name" class="form-control" [(ngModel)]="serverName"> <label for="description">توضیحات سرور</label> <input type="text" id="description" class="form-control" [(ngModel)]="serverContent"> </div> </div>
همانطور که مشاهده میکنید در این فرم نام سرور و توضیحات به صورت ارتباط دو طرفه به کنترلر یا کلاس کامپوننت ارسال میشد. حال برای تعریف یک قسمت از این قالب به صورت Local Reference به صورت زیر عمل میکنیم:
<div class="row"> <div class="col-xs-12"> <label for="name">نام سرور</label> <input type="text" id="name" class="form-control" #localServerName> <label for="description">توضیحات سرور</label> <input type="text" id="description" class="form-control" [(ngModel)]="serverContent"> </div> </div>
در این مثال فرم «نام سرور» به صورت لوکال رفرنس یا ارجاع داخلی طراحی شد. برای اینکار کافیست از یک هشتگ (#) استفاده کرده و روبهروی آن نام هشتگ را قرار دهیم.
حال این بدین معنیست که این قسمت از قالب ما که شامل هشتگ میشود به عنوان یک فرم در هر جای برنامه قابل دسترس است. برای تکمیل شدن این بحث مثال فوق را گسترش داده و عبارتهای زیر را نیز به آن میافزاییم:
<div class="row"> <div class="col-xs-12"> <label for="name">نام سرور</label> <input type="text" id="name" class="form-control" #localServerName> <label for="description">توضیحات سرور</label> <input type="text" id="description" class="form-control" [(ngModel)]="serverContent"> </div> </div> <div class="row"> <div class="col-xs-12"> <button class="btn btn-primary" (click)="onCreateLocalServer(localServerName)">سرور داخلی جدید</button> <button class="btn btn-primary" (click)="onCreateExternalServer()">سرور خارجی جدید</button> </div> </div> <div class="row"> <hr> <div class="col-xs-12"> <div class="panel panel-default"> <div class="panel-heading">{{initServer.name}}</div> <div class="panel-body">{{initServer.content}}</div> </div> </div> </div> <div class="row"> <hr> <div class="col-xs-12"> <div class="panel panel-default" *ngFor="let server of serverList"> <div class="panel-heading">{{server.name}}</div> <div class="panel-body">{{server.content}}</div> </div> </div> </div>
با بررسی متوجه خواهید شد که به هنگام کلیک روی دکمهی «سرور داخلی» یک رویداد یا event رخ میدهد و در این حین متد onLocalServer اجرا شده با این تفاوت که یک آرگومان به عنوان ورودی به آن ارسال میشود. این آرگومان چیزی نیست جز قسمتی از قالب که به صورت local reference طراحی شده است. حال به بخش کلاس کامپوننت این مثال باز میگردیم:
import {Component, OnInit, Input, EventEmitter, Output, ViewEncapsulation} from '@angular/core'; @Component({ selector: 'app-server', templateUrl: './server.component.html', styleUrls: ['./server.component.css'], encapsulation: ViewEncapsulation.Native }) export class ServerComponent implements OnInit { serverName: string = ''; serverContent: string = ''; serverList = []; @Input('initS') initServer = {name: "روکسو", content: "این سرور باید به کامپوننت والد ارسال شود"}; @Output() localServerCreated = new EventEmitter<{ serverName: string, serverContent: string }>(); @Output('exServerCreated') externalServerCreated = new EventEmitter<{ serverName: string, serverContent: string }>() constructor() { } ngOnInit() { } onCreateLocalServer(localTemplate) { console.log(localTemplate) // this.localServerCreated.emit({ // serverName: this.serverName, // serverContent: this.serverContent // }); } onCreateExternalServer() { this.externalServerCreated.emit({ serverName: this.serverName, serverContent: this.serverContent }); } }
با بررسی این فایل متوجه خواهید شد که یک متد به نام onCreateLocalServer ایجاد کرده که یک ورودی به نام localTemplate دریافت میکند.
حال به شما توصیه میکنیم که در ابتدا برای تست کردن و مشاهده محتویات درون این آرگومان ابتدا آن را با استفاده از دستور console.log در صفحه مرورگر خود و با ابزار توسعه نمایش دهید.
بنابراین وقتی شما صفحه مرورگر خود را باز کنید با صفحهای مشابه زیر روبهرو خواهید شد:
بنابراین وقتی آرگومان ارسالی به متد onCreateLocalServer را نمایش میدهیم تگ HTML ورودی input را به همراه صفاتش برای شما به نمایش میگذارد بنابراین با این روش یک قسمت از قالب را به عنوان لوکال رفرنس تعیین کردیم.
اما نکتهی جالب اینجاست که شما میتوانید به مقادیر درون این قالبها با استفاده از دستور value. دسترسی داشته باشید بنابراین درون console.log خواهیم داشت:
console.log(localTemplate.value)
با اجرای برنامه و وارد کردن نام یک سرور در نهایت خروجی شما به صورت زیر نمایش داده میشود:
بسیار عالی در این قسمت شما توانستید اطلاعات و دادهها را به فرم دیگری به نام لوکال رفرنس به کلاس کامپوننت خود ارسال کنید. بنابراین در اینجا به جای استفاده از ngModel، از LocalReference ها بهره بردیم.
در ادامه شیوهی دیگر ارسال داده از طریق قالب HTML به کلاس کامپوننت را خدمت شما عزیزان ارائه میدهیم که مجددا با استفاده از localReference ها صورت میپذیرد.
مفسر یا دکوراتور ViewChild به ما کمک میکند که لوکال رفرنس خود را به عنوان یک ویژگی مورد استفاده قرار دهیم.
برای تفهیم بیشتر مثال قبل را مجددا مورد تغییر و بررسی قرار میدهیم. در قسمت مربوط به قالب HTML میخواهیم لوکال رفرنس را بهگونهای تعریف کنیم که در صورت ورود نام سرور و توضیحات مربوطه، آن را در انتهای صفحه نمایش دهد.
بنابراین تغییرات زیر را در قالب HTML خواهیم داشت:
<div class="row"> <div class="col-xs-12"> <label for="name">نام سرور</label> <input type="text" id="name" class="form-control" #referenceServerName> <label for="description">توضیحات سرور</label> <input type="text" id="description" class="form-control" #referenceServerContent> </div> </div> <div class="row"> <div class="col-xs-12"> <button class="btn btn-primary" (click)="onCreateLocalServer()">سرور داخلی جدید</button> <button class="btn btn-primary" (click)="onCreateExternalServer()">سرور خارجی جدید</button> </div> </div> <div class="row"> <hr> <div class="col-xs-12"> <div class="panel panel-default" *ngFor="let server of serverList"> <div class="panel-heading">{{server.name}}</div> <div class="panel-body">{{server.content}}</div> </div> </div> </div>
یعنی بخشهایی که به صورت ngModel تبادل داده میکردند را حذف کرده و به جای آنها برای هر ورودی یک لوکال رفرنس تعریف کردهایم.
شاید برای شما جالب باشد که چرا درون متدهایی که به هنگام event کلیک، رخ میدهند، آرگومانی ارسال نشده است؟
برای پاسخ به این سوال باید بگوییم که وظیفهی دریافت اطلاعات از لوکال رفرنسها هماکنون به عهدهی مفسر یا دکوراتور ViewChild میباشد.
بنابراین در فایل component.ts خواهیم داشت:
import {Component, OnInit, Input, EventEmitter, Output, ViewChild, ElementRef} from '@angular/core'; @Component({ selector: 'app-server', templateUrl: './server.component.html', styleUrls: ['./server.component.css'], }) export class ServerComponent implements OnInit { serverName: string = ''; serverContent: string = ''; serverList = []; @Input('initS') initServer = {name: "روکسو", content: "این سرور باید به کامپوننت والد ارسال شود"}; @Output() localServerCreated = new EventEmitter<{ serverName: string, serverContent: string }>(); @Output('exServerCreated') externalServerCreated = new EventEmitter<{ serverName: string, serverContent: string }>() @ViewChild('referenceServerName') referenceServerName: ElementRef; @ViewChild('referenceServerContent') referenceServerContent: ElementRef; constructor() { } ngOnInit() { } onCreateLocalServer() { this.localServerCreated.emit({ serverName: this.referenceServerName.nativeElement.value, serverContent: this.referenceServerContent.nativeElement.value }); } onCreateExternalServer() { this.externalServerCreated.emit({ serverName: this.referenceServerName.nativeElement.value, serverContent: this.referenceServerContent.nativeElement.value }); } }
در این فایل ابتدا دو ویژگی یا property به نام referenceServerName و referenceServerContent با استفاده از مفسر ViewChild تعریف کردهایم که نوع آنها از جنس ElementRef میباشد (زیرا متغییریست که به یک لوکال رفرنس اشاره میکند). حال درون مفسر ViewChild یک سلکتور تعریف کردهایم که نمایانگر قسمتی از قالب است که به آن رجوع میکنیم. توجه داشته باشید که این عناوین دقیقا باید مشابه آنچه در قالب قرار دارد، تعریف شوند.
در متدهای onCreateLocalServer و onCreateExternalServer، آرگومانها را حذف کرده و به جای آن از ویژگیهایی که تعریف کردهایم بهره بردهایم. و سپس با دسترسی به nativeElement (که به عنوان یک ویژگی در دادههایی با جنس ElementRef تعریف میشود، کاربرد دارد) و در نهایت مقدار value، آنچه درون فرم است برای متد موردنظر ارسال میشود. در صورتیکه موارد فوق را به درستی انجام داده باشید خروجی شما به صورت زیر خواهد بود:
گاهی نیاز دارید که یک قسمت از قالب یک کامپوننت را به صورت کاملا واضح و مشخص درون تگ سلکتور آن کامپوننت که در قالب HTML سایر کامپوننتها قرار دارد، مورد استفاده قرار دهید. در این صورت یک تگ سلکتور که به صورت <ng-content></ng-content>
تعریف میشود در اختیار شما قرار گرفته و میتوانید این کار را به سادهترین شکل ممکن انجام دهید.
برای مثال فرض کنید میخواهیم حلقهی یک تگ div در مثال قبل را که درون کامپوننت server.component.html قرار دارد را درون کامپوننت والد (servers) نمایش دهیم. در اینصورت فایل servers.component.html را باز کرده و کدهای زیر را به آن میافزایم:
<app-server [initS]="changeServerList" (localServerCreated)="onLocalServerAdded($event)" (exServerCreated)="onExternalServerAdded($event)" > <p>این مطلب از کامپوننت فرزند تولید و اینجا قرار داده شده است.</p> </app-server>
بنابراین درون قالب server.component.html باید تگ ng-content را وارد کنیم:
<div class="row"> <hr> <div class="col-xs-12"> <div class="panel panel-default" *ngFor="let server of serverList"> <div class="panel-heading">{{server.name}}</div> <div class="panel-body">{{server.content}}</div> </div> <ng-content></ng-content> </div> </div>
در صورتیکه این کد را اجرا کنید مشاهده خواهید کرد آنچه درون قالب HTML کامپوننت والد تعریف شده بود در قسمت ng-content نمایش داده خواهد شد.
بسیار عالی به شما تبریک میگوییم با مطالعهی این فصل اطلاعات عمیقی مبتنی بر لوکال رفرنسها و همچنین تگ ng-content در اختیار شما قرار گرفت. به عبارت دیگر راهحل دومی برای ارتباط و انتقال داده به کامپوننتها مطرح شد. در جلسهی بعدی چرخه حیات یک کامپوننت را به صورت حرفهای بررسی میکنیم. با ما همراه باشید.
توجه: دوستان عزیز آموزش ویدیویی انگولار 6 از مقدماتی تا پیشرفته به زبان فارسی را میتوانید با کلیک روی اینجا یاد بگیرید
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.