همانطور که از اسم آن نیز مشخص است، ng-template به معنای قالب انگیولار است و هدف از آن، ارائهی قسمتی از قالب نهایی یک کامپوننت میباشد. فریم ورک Angular از دایرکتیو ng-template در پشت صحنهی دایرکتیوهای ساختاری مانند ngIf، ngFor و ngSwitch استفاده میکند. برای مثال، قسمت if، تبدیل به یک ng-template شده و else آن نیز تبدیل به یک ng-template ضمنی دیگر خواهد شد.
روش فعالسازی و نمایش قالبها
باید دقت داشت که تعریف یک ng-template سبب رندر هیچگونه خروجی در صفحه نمیشود و باید به طریقی درخواست فعالسازی و رندر آنرا ارائه داد.
<div class="lessons-list" *ngIf="lessons else loading"> ... </div> <ng-template #loading> <div>Loading...</div> </ng-template>
هرچند در پشت صحنه برای حالت ngIf نیز یک ng-template ضمنی محصور کنندهی div اصلی تشکیل میشود که از دید ما پنهان است.
استفاده از ngIf برای نمایش یک قالب، یکی از روشهای کار با آنها است. روش دیگر، استفاده از ng-container است:
<ng-container *ngTemplateOutlet="loading"></ng-container>
سطوح دسترسی در قالبها
اکنون این سؤال مطرح است: «آیا یک قالب میدان دید متغیرهای خاص خودش را دارد؟ این قالب به چه متغیرهایی دسترسی دارد؟»
درون بدنه یک تگ ng-template، به همان متغیرهایی که در قالب خارجی آن قابل دسترسی هستند، دسترسی خواهیم داشت؛ برای نمونه در مثال فوق به همان متغیر lessons. به عبارتی تمام وهلههای ng-templateها، به همان متغیرهای زمینهی قالبی که درون آن جایگرفتهاند، دسترسی دارند. به علاوه هر قالب میتواند متغیرهای خاص خود را نیز تعریف کند.
در ادامه قالب یک کامپوننت را به صورت ذیل فرض کنید:
<ng-template #estimateTemplate let-lessonsCounter="estimate"> <div> Approximately {{lessonsCounter}} lessons ...</div> </ng-template> <ng-container *ngTemplateOutlet="estimateTemplate;context:ctx"> </ng-container>
export class AppComponent { totalEstimate = 10; ctx = {estimate: this.totalEstimate}; }
این متغیر lessonsCounter تنها داخل این قالب است که قابل مشاهده و دسترسی میباشد و نه خارج از آن. مقدار این متغیر نیز توسط عبارت estimate تامین میشود. این عبارت زمانیکه ng-container سبب وهله سازی estimateTemplate میشود، توسط شیء ویژهای به نام context مقدار دهی خواهد شد.
برای اینکه عبارت estimate در قالب، قابل استخراج از شیء context باشد، باین دقیقا خاصیتی به همین نام در این شیء تعریف شده باشد (و برای سایر متغیرها نیز به همین ترتیب). به همین جهت است که خاصیت عمومی ctx در کلاس AppComponent به صورت یک شیء دارای خاصیت estimate تعریف شدهاست تا بتوان نگاشتی را بین این مقدار و عبارت estimate برقرار کرد.
نکته 1: اگر در اینجا متغیری تعریف شود، اما محل تامین آن مشخص نگردد، به دنبال خاصیتی به نام implicit$ خواهد گشت. برای مثال در قالب ذیل، متغیر default تعریف شدهاست؛ اما عبارت تامین کنندهی آن مشخص نیست:
<ng-container *ngTemplateOutlet="templateRef; context: exampleContext"></ng-container> <ng-template #templateRef let-default> <div> '{{default}}' </div> </ng-template>
export class AppComponent { exampleContext = { $implicit: 'default context property when none specified' }; }
نکته 2: نحوهی تعریف شیء context را به صورت ذیل نیز میتوان مشخص کرد:
[ngOutletContext]="exampleContext"
دسترسی به قالبها در کدهای کامپوننتها
در اینجا قالبی را مشاهده میکنید که توسط یک template reference variable به نام defaultTabButtons مشخص شدهاست:
<ng-template #defaultTabButtons> </ng-template>
export class AppComponent implements OnInit { @ViewChild('defaultTabButtons') private defaultTabButtonsTpl: TemplateRef<any>; ngOnInit() { console.log(this.defaultTabButtonsTpl); } }
یکی از کاربردهای این قابلیت، امکان تعویض پویای قالبهای یک دربرگیرندهاست:
<ng-container *ngTemplateOutlet="headerTemplate ? headerTemplate: defaultTabButtons"> </ng-container>
در اینجا headerTemplate خاصیتی است عمومی از نوع TemplateRef که در کدهای کامپوننت متناظر با این قالب مقدار دهی میشود. اگر این مقدار دهی صورت نگیرد، از قالب از پیش موجود defaultTabButtons استفاده خواهد کرد.
همچنین اگر میخواهیم به selector یک کامپوننت قابلیت انتخاب قالبی را بدهیم میتوان یک خاصیت عمومی مزین شدهی با Input از نوع TemplateRef را مشخص کرد:
@Input() headerTemplate: TemplateRef<any>;
<tab-container [headerTemplate]="defaultTabButtons"></tab-container>