1.ng-template指令介紹--<ng-template></ng-template>
ng-template表示一個模板,標簽內是模板的內容,模板的內容可以與其它模板一起組成組件模板。
在Angular中,我們用過的許多結構指令都使用了ng-template,如ngIf、ngFor和ngSwitch。
ngTemplate中的內容可以自定義,並且一開始不會被渲染,除非滿足一定的條件。我們必須使用結構指令去渲染它。
上面代碼在頁面中並沒有被渲染。
2.ng-template使用
2.1ng-template與ngIf
我們見的最多的可能是與ngIf一塊使用,當if條件不成立時,將顯示else中的內容。下面代碼中else語句被指向了一個名為loading的模板,通過模板引用#loading的方式。
1 <div class="lessons-list" *ngIf="lessons else loading"> 2 ... 3 </div> 4 5 <ng-template #loading> 6 <div>Loading...</div> 7 </ng-template>
其實,ngIf指令也是ng-template的包裝。
1 <ng-template [ngIf]="lessons" [ngIfElse]="loading"> 2 <div class="lessons-list"> 3 ... 4 </div> 5 </ng-template> 6 7 <ng-template #loading> 8 <div>Loading...</div> 9 </ng-template>
[ngIf]和[ngIfElse]為模板輸入變量。
2.2同一元素使用多個結構指令
1 <div class="lesson" *ngIf="lessons" 2 *ngFor="let lesson of lessons"> 3 <div class="lesson-detail"> 4 {{lesson | json}} 5 </div> 6 </div>
這樣會報錯:
Uncaught Error: Template parse errors: Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *
這意味着不能在同一個元素使用兩個或多個結構指令。
1 <div *ngIf="lessons"> 2 <div class="lesson" *ngFor="let lesson of lessons"> 3 <div class="lesson-detail"> 4 {{lesson | json}} 5 </div> 6 </div> 7 </div>
可以在ngFor指令的外面包一層ngIf指令,這樣就解決了上面的問題;但這樣會額外創一個一個元素。因此,我們可以使用ng-container指令。
1 <ng-container *ngIf="lessons"> 2 <div class="lesson" *ngFor="let lesson of lessons"> 3 <div class="lesson-detail"> 4 {{lesson | json}} 5 </div> 6 </div> 7 </ng-container>
ng-container的一種用途是,給我們提供一個附加指令的元素而不會創建一個額外的元素。另一種用途是,配合ngTemplateOutlet使用。
3.ngTemplateOutlet使用介紹
ngTemplateOutlet也是一個指令,它使用模板引用和上下文對象作為參數動態實例化模板。作為一個結構指令,我們可以使用它在DOM的各個部分插入模板(由ng-template創建)。
1 <ng-container *ngTemplateOutlet="loading">This text is not displayed</ng-container>
ngTemplateOutlet包含的任何內部內容都不會被渲染;我們可以根據需要向頁面添加任意數量的ngTemplateOutlet標簽,並實例化許多不同的模板。
1 <div *ngTemplateOutlet="loading"></div>
上面代碼中的div並不會被渲染,因為angular將上述內容替換成ng-template語法,
1 <ng-template [ngTemplateOutlet]="template1"><div></div></ng-template>
3.1傳遞數據給ngTemplateOutlet
我們可以通過ngTemplateOutletContext屬性傳遞數據
1 <ng-template let-value="value" #messageTemplate> 2 <p>Value Received from the Parent is {{value}}</p> 3 </ng-template>
1 <ng-container [ngTemplateOutlet]="messageTemplate" 2 [ngTemplateOutletContext] ="{value:'1000'}"> 3 </ng-container>
當然,也可以使用簡寫屬性--context:
1 <ng-container *ngTemplateOutlet="messageTemplate; context:{value:100}"></ng-container> 傳遞多個數據:
1 <ng-template let-name="nameVar" let-message="messageVar" #template> 2 <p>Dear {{name}} , {{message}} </p> 3 </ng-template> 4 5 6 <ng-container [ngTemplateOutlet]="template" 7 [ngTemplateOutletContext] ="{nameVar:'Guest',messageVar:'Welcome to our site'}"> 8 </ng-container>
傳遞對象:
1 2 <ng-template let-person="person" #template> 3 <p>Dear {{person.name}} , {{person.message}} </p> 4 </ng-template> 5 6 7 <ng-container [ngTemplateOutlet]="template" 8 [ngTemplateOutletContext] ="{ person:{name:'Guest',message:'Welcome to our site'} }"> 9 </ng-container>
使用$implicit:
可以在上下文對象中使用$implicit將其值設置為所有局部變量的默認值。
1 <ng-template let-name let-message="message" #template3> 2 <p>Dear {{name}} , {{message}} </p> 3 </ng-template> 4 5 <ng-container [ngTemplateOutlet]="templates" 6 [ngTemplateOutletContext] ="{$implicit:'Guest',message:'Welcome to our site'}"> 7 </ng-container>
上面代碼中,我們沒有給 let-name 賦值,因此它從$implicit中獲取值。
3.2模板上下文
每個模板可以定義自己的輸入變量;實際上,每個模板都關聯了一個上下文對象,其中包含所有特定於模板的輸入變量。
1 @Component({ 2 selector: 'app-root', 3 template: ` 4 <ng-template #estimateTemplate let-lessonsCounter="estimate"> 5 <div> Approximately {{lessonsCounter}} lessons ...</div> 6 </ng-template> 7 <ng-container 8 *ngTemplateOutlet="estimateTemplate;context:ctx"> 9 </ng-container> 10 `}) 11 export class AppComponent { 12 13 totalEstimate = 10; 14 ctx = {estimate: this.totalEstimate}; 15 16 }
說明:lessonsCounter為輸入變量,通過ng-template屬性使用前綴 let- 定義的
lessonsCounter對ng-template模板里面的內容是可見的,對模板外是不可見的。
lessonsCounter的值是由 estimate 決定的,故context 對象必須有一個estimate 的屬性。
3.3 模板引用
我們可以使用ViewChild裝飾器將模板直接注入到組件中:
1 @Component({ 2 selector: 'app-root', 3 template: ` 4 <ng-template #defaultTabButtons> 5 <button class="tab-button" (click)="login()"> 6 {{loginText}} 7 </button> 8 <button class="tab-button" (click)="signUp()"> 9 {{signUpText}} 10 </button> 11 </ng-template> 12 `}) 13 export class AppComponent implements OnInit { 14 15 @ViewChild('defaultTabButtons') 16 private defaultTabButtonsTpl: TemplateRef<any>; 17 18 ngOnInit() { 19 console.log(this.defaultTabButtonsTpl); //undefined; ngAfterViewInit()才會打印 20 } 21 22 }
模板可以被注入,就像DOM元素和組件一樣,通過將模板引用傳給ViewChild裝飾器。
我們還可以將模板作為一個輸入變量:
1 @Component({ 2 selector: 'app-root', 3 template: ` 4 <ng-template #customTabButtons> 5 <div class="custom-class"> 6 <button class="tab-button" (click)="login()"> 7 {{loginText}} 8 </button> 9 <button class="tab-button" (click)="signUp()"> 10 {{signUpText}} 11 </button> 12 </div> 13 </ng-template> 14 <tab-container [headerTemplate]="customTabButtons"></tab-container> 15 `}) 16 export class AppComponent implements OnInit { 17 18 }
1 @Component({ 2 selector: 'tab-container', 3 template: ` 4 5 <ng-template #defaultTabButtons> 6 7 <div class="default-tab-buttons"> 8 ... 9 </div> 10 11 </ng-template> 12 <ng-container 13 *ngTemplateOutlet="headerTemplate ? headerTemplate: defaultTabButtons"> 14 15 </ng-container> 16 ... rest of tab container component ... 17 `}) 18 export class TabContainerComponent { 19 @Input() 20 headerTemplate: TemplateRef<any>; 21 }
當我們傳入自定義模板時(#customTabButtons),就顯示這個自定義模板;否則,顯示默認的模板。
3.4 ngTemplateOutlet、ng-template 與內容投影
父組件使用內容投影傳遞一個模板給子組件:
1 import { Component, TemplateRef, Input, OnInit, ViewChild, AfterViewInit } from '@angular/core'; 2 3 @Component({ 4 selector: 'parent1', 5 template: ` 6 7 <h1>Parent Component </h1> 8 9 <child1> 10 <p>This Template is Projected to the Child</p> 11 </child1> 12 ` 13 }) 14 export class Parent1Component { 15 }
子組件:
1 import { Component, TemplateRef, Input, OnInit, ViewChild, AfterViewInit } from '@angular/core'; 2 3 @Component({ 4 selector: 'child1', 5 template: ` 6 7 <h1>Child Component </h1> 8 9 <ng-template #parentTemplate> 10 <ng-content></ng-content> 11 </ng-template> 12 13 <ng-template [ngTemplateOutlet]="parentTemplate"></ng-template> 14 15 ` 16 }) 17 export class Child1Component { 18 } 19
4.總結
ng-template、ng-container和ngTemplateOutlet指令結合使用,可以創建高度動態和可定制的組件。