angular之ng-template模板載入,angularng-template
本文介紹了angular之ng-template模板載入,分享給大家,具體如下:
html5中的template
template標籤的含義:HTML <template>元素是一種用於儲存用戶端內容的機制,該內容在頁面載入時是不可見的,但可以在運行時使用JavaScript進行執行個體化,可以將一個模板視為正在被儲存以供隨後在文檔中使用的一個內容片段。
屬性
此元素僅包含全域屬性和唯讀 content 屬性,通過content 可以讀模數板內容,而且可以通過判斷 content 屬性是否存在來判斷瀏覽器是否支援 <template> 元素。
樣本
html
<table id="producttable"> <thead> <tr> <td>UPC_Code</td> <td>Product_Name</td> </tr> </thead> <tbody> <!-- 現有資料可以可選地包括在這裡 --> </tbody></table><template id="productrow"> <tr> <td class="record"></td> <td></td> </tr></template>
js
// 通過檢查來測試瀏覽器是否支援HTML模板元素 // 用於儲存模板元素的內容屬性。if ('content' in document.createElement('template')) { // 使用現有的HTML tbody執行個體化表和該行與模板 let t = document.querySelector('#productrow'), td = t.content.querySelectorAll("td"); td[0].textContent = "1235646565"; td[1].textContent = "Stuff"; // 複製新行並將其插入表中 let tb = document.getElementsByTagName("tbody"); let clone = document.importNode(t.content, true); tb[0].appendChild(clone); // 建立一個新行 td[0].textContent = "0384928528"; td[1].textContent = "Acme Kidney Beans"; // 複製新行並將其插入表中 let clone2 = document.importNode(t.content, true); tb[0].appendChild(clone2);} else { // 找到另一種方法來添加行到表,因為不支援HTML模板元素。}
代碼運行後,結果將是一個包含(由 JavaScript 產生)兩個新行的 HTML 表格:
UPC_Code Product_Name1235646565 Stuff0384928528 Acme Kidney Beans
注釋掉 tb[0].appendChild(clone);和tb[0].appendChild(clone2);,運行代碼,只會看到:
UPC_Code Product_Name
說明template元素中的內容如果不經過處理,瀏覽器是不會渲染的。
angular中的ng-template
<ng-template>是一個 Angular 元素,它永遠不會直接顯示出來。在渲染視圖之前,Angular 會把<ng-template>及其內容替換為一個注釋。
以ngIf為例:
<ng-template> 模板元素與html5的template元素一樣,需要被特殊處理後才能渲染。ng主要是通過類TemplateRef和ViewContainerRef實現的。
通過閱讀ngIf源碼學習如何運用<ng-template>
在使用ngIf 指令時我們並未發現ng-template的身影,這是因為"*"(星號)文法糖的原因,這個簡寫方法是一個微文法,而不是通常的模板運算式, Angular會解開這個文法糖,變成一個<ng-template>標記,包裹著宿主要元素及其子項目。
<div *ngIf="hero" >{{hero.name}}</div>
會被解析為
<ng-template [ngIf]="hero"> <div>{{hero.name}}</div></ng-template>`
看下ngIf源碼
import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '@angular/core';@Directive({selector: '[ngIf]'})export class NgIf { private _context: NgIfContext = new NgIfContext(); private _thenTemplateRef: TemplateRef<NgIfContext>|null = null; private _elseTemplateRef: TemplateRef<NgIfContext>|null = null; private _thenViewRef: EmbeddedViewRef<NgIfContext>|null = null; private _elseViewRef: EmbeddedViewRef<NgIfContext>|null = null; constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>) { this._thenTemplateRef = templateRef; } @Input() set ngIf(condition: any) { this._context.$implicit = this._context.ngIf = condition; this._updateView(); } @Input() set ngIfThen(templateRef: TemplateRef<NgIfContext>) { this._thenTemplateRef = templateRef; this._thenViewRef = null; // clear previous view if any. this._updateView(); } @Input() set ngIfElse(templateRef: TemplateRef<NgIfContext>) { this._elseTemplateRef = templateRef; this._elseViewRef = null; // clear previous view if any. this._updateView(); } private _updateView() { if (this._context.$implicit) { if (!this._thenViewRef) { this._viewContainer.clear(); this._elseViewRef = null; if (this._thenTemplateRef) { this._thenViewRef = this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context); } } } else { if (!this._elseViewRef) { this._viewContainer.clear(); this._thenViewRef = null; if (this._elseTemplateRef) { this._elseViewRef = this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context); } } } }}export class NgIfContext { public $implicit: any = null; public ngIf: any = null;}
ngIf的源碼並不難,它的核心就在於_updateView函數,它主要通過ViewContainerRef的createEmbeddedView和clear方法來實現模板TemplateRef的呈現和清除(先不關注當中的then和else等的具體實現)。它使用TemplateRef取得<ng-template>的內容,並通過ViewContainerRef來訪問這個視圖容器。
TemplateRef
TemplateRef 執行個體用於表示模板對象,TemplateRef 抽象類別的定義如下:
abstract get elementRef(): ElementRef; abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;}
在指令中通過依賴注入TemplateRef可以直接拿到ng-tempalte的TemplateRef,但是在component組件中我們則需要使用viewChild
<ng-template #tptest> <span>template test</span></ng-template>@ViewChild('tptest') tptest: TemplateRef<any>;
ViewContainerRef
ViewContainerRef 執行個體提供了 createEmbeddedView() 方法,該方法接收 TemplateRef 對象作為參數,並將模板中的內容作為容器 (comment 元素) 的兄弟元素,插入到頁面中。
export abstract class ViewContainerRef { /*基於TemplateRef對象建立Embedded View(內嵌視圖),然後根據`index`指定的值,插入到容器中。 如果沒有指定`index`的值,新建立的視圖將作為容器中的最後一個視圖插入。*/ abstract createEmbeddedView<C>( templateRef: TemplateRef<C>, //內嵌視圖 context?: C, index?: number): // 建立上下文 EmbeddedViewRef<C>;}
createEmbeddedView:context
建立Template 自身 Context 的屬性,以ngFor為例:
查看ngFor Context源碼:
export class NgForOfContext<T> { constructor( public $implicit: T, public ngForOf: NgIterable<T>, public index: number, public count: number) {} get first(): boolean { return this.index === 0; } get last(): boolean { return this.index === this.count - 1; } get even(): boolean { return this.index % 2 === 0; } get odd(): boolean { return !this.even; }}
<div *ngFor="let hero of heroes; let i=index; let odd=odd"> ({{i}}) {{hero.name}}</div>解析後:<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" > <div>({{i}}) {{hero.name}}</div></ng-template>
從例子中可以看到,通過let-i let-odd可以擷取到Template的context,這是angular提供的一種文法。因為在 Angular中是沒有範圍繼承的,所以在模版中無法隱式實現兩個無關資料來源。一個簡單的實現方案就是:一個顯式、一個隱式。由於ng-template tag 是寫在某個 Component 的 template屬性中的,所以在 ng-template tag 之下的部分當然能訪問的也只有 Component 作為 Context 提供的屬性,從而保持行為的一致性,而如果需要訪問到 Template 的 Context,我們就需要使用額外的引入文法。比如 let-i="index",就是把 Template Context 中的 index屬性引入到當前的 Component Context 中並賦予別名 i,這樣,我們就能夠使用 i 這個標識符來訪問到 Template Context 中的屬性了,並且仍然保持了行為的一致性和範圍的獨立性。
模板輸入變數是這樣一種變數,你可以在單個執行個體的模板中引用它的值。 這個例子中有好幾個模板輸入變數:hero、i和odd。 它們都是用let作為前置關鍵字。
模板輸入變數和模板引用變數是不同的,無論是在語義上還是文法上。
我們使用let關鍵字(如let hero)在模板中聲明一個模板輸入變數。 這個變數的範圍被限制在所重複模板的單一執行個體上。
而聲明模板引用變數使用的是給變數名加#首碼的方式(#var)。 一個引用變數引用的是它所附著到的元素、組件或指令。它可以在整個模板任意位置**訪問。
模板輸入變數和引用變數具有各自獨立的命名空間。let hero中的hero和#hero中的hero並不是同一個變數。
總結:
<ng-template>在ng中主要通過viewChild TemplateRef ViewContainerRef來實現結構性操作。
以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援幫客之家。