angular之ng-template模板加載
本文介紹了angular之ng-template模板加載,分享給大家,具體如下:
html5中的template
template標(biāo)簽的含義:HTML <template>元素是一種用于保存客戶端內(nèi)容的機(jī)制,該內(nèi)容在頁面加載時(shí)是不可見的,但可以在運(yùn)行時(shí)使用JavaScript進(jìn)行實(shí)例化,可以將一個(gè)模板視為正在被存儲以供隨后在文檔中使用的一個(gè)內(nèi)容片段。

屬性
此元素僅包含全局屬性和只讀的 content 屬性,通過content 可以讀取模板內(nèi)容,而且可以通過判斷 content 屬性是否存在來判斷瀏覽器是否支持 <template> 元素。
示例
html
<table id="producttable"> <thead> <tr> <td>UPC_Code</td> <td>Product_Name</td> </tr> </thead> <tbody> <!-- 現(xiàn)有數(shù)據(jù)可以可選地包括在這里 --> </tbody> </table> <template id="productrow"> <tr> <td class="record"></td> <td></td> </tr> </template>
js
// 通過檢查來測試瀏覽器是否支持HTML模板元素
// 用于保存模板元素的內(nèi)容屬性。
if ('content' in document.createElement('template')) {
// 使用現(xiàn)有的HTML tbody實(shí)例化表和該行與模板
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);
// 創(chuàng)建一個(gè)新行
td[0].textContent = "0384928528";
td[1].textContent = "Acme Kidney Beans";
// 克隆新行并將其插入表中
let clone2 = document.importNode(t.content, true);
tb[0].appendChild(clone2);
} else {
// 找到另一種方法來添加行到表,因?yàn)椴恢С諬TML模板元素。
}
代碼運(yùn)行后,結(jié)果將是一個(gè)包含(由 JavaScript 生成)兩個(gè)新行的 HTML 表格:
UPC_Code Product_Name 1235646565 Stuff 0384928528 Acme Kidney Beans
注釋掉 tb[0].appendChild(clone);和tb[0].appendChild(clone2);,運(yùn)行代碼,只會看到:
UPC_Code Product_Name
說明template元素中的內(nèi)容如果不經(jīng)過處理,瀏覽器是不會渲染的。
angular中的ng-template
<ng-template>是一個(gè) Angular 元素,它永遠(yuǎn)不會直接顯示出來。在渲染視圖之前,Angular 會把<ng-template>及其內(nèi)容替換為一個(gè)注釋。
以ngIf為例:

<ng-template> 模板元素與html5的template元素一樣,需要被特殊處理后才能渲染。ng主要是通過類TemplateRef和ViewContainerRef實(shí)現(xiàn)的。
通過閱讀ngIf源碼學(xué)習(xí)如何運(yùn)用<ng-template>
在使用ngIf 指令時(shí)我們并未發(fā)現(xiàn)ng-template的身影,這是因?yàn)?*"(星號)語法糖的原因,這個(gè)簡寫方法是一個(gè)微語法,而不是通常的模板表達(dá)式, Angular會解開這個(gè)語法糖,變成一個(gè)<ng-template>標(biāo)記,包裹著宿主元素及其子元素。
<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函數(shù),它主要通過ViewContainerRef的createEmbeddedView和clear方法來實(shí)現(xiàn)模板TemplateRef的呈現(xiàn)和清除(先不關(guān)注當(dāng)中的then和else等的具體實(shí)現(xiàn))。它使用TemplateRef取得<ng-template>的內(nèi)容,并通過ViewContainerRef來訪問這個(gè)視圖容器。
TemplateRef
TemplateRef 實(shí)例用于表示模板對象,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 實(shí)例提供了 createEmbeddedView() 方法,該方法接收 TemplateRef 對象作為參數(shù),并將模板中的內(nèi)容作為容器 (comment 元素) 的兄弟元素,插入到頁面中。
export abstract class ViewContainerRef {
/*基于TemplateRef對象創(chuàng)建Embedded View(內(nèi)嵌視圖),然后根據(jù)`index`指定的值,插入到容器中。
如果沒有指定`index`的值,新創(chuàng)建的視圖將作為容器中的最后一個(gè)視圖插入。*/
abstract createEmbeddedView<C>(
templateRef: TemplateRef<C>, //內(nèi)嵌視圖
context?: C, index?: number): // 創(chuàng)建上下文
EmbeddedViewRef<C>;
}
createEmbeddedView:context
創(chuàng)建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提供的一種語法。因?yàn)樵?Angular中是沒有作用域繼承的,所以在模版中無法隱式實(shí)現(xiàn)兩個(gè)無關(guān)數(shù)據(jù)源。一個(gè)簡單的實(shí)現(xiàn)方案就是:一個(gè)顯式、一個(gè)隱式。由于ng-template tag 是寫在某個(gè) Component 的 template屬性中的,所以在 ng-template tag 之下的部分當(dāng)然能訪問的也只有 Component 作為 Context 提供的屬性,從而保持行為的一致性,而如果需要訪問到 Template 的 Context,我們就需要使用額外的引入語法。比如 let-i="index",就是把 Template Context 中的 index屬性引入到當(dāng)前的 Component Context 中并賦予別名 i,這樣,我們就能夠使用 i 這個(gè)標(biāo)識符來訪問到 Template Context 中的屬性了,并且仍然保持了行為的一致性和作用域的獨(dú)立性。
模板輸入變量是這樣一種變量,你可以在單個(gè)實(shí)例的模板中引用它的值。 這個(gè)例子中有好幾個(gè)模板輸入變量:hero、i和odd。 它們都是用let作為前導(dǎo)關(guān)鍵字。
模板輸入變量和模板引用變量是不同的,無論是在語義上還是語法上。
我們使用let關(guān)鍵字(如let hero)在模板中聲明一個(gè)模板輸入變量。 這個(gè)變量的范圍被限制在所重復(fù)模板的單一實(shí)例上。
而聲明模板引用變量使用的是給變量名加#前綴的方式(#var)。 一個(gè)引用變量引用的是它所附著到的元素、組件或指令。它可以在整個(gè)模板任意位置**訪問。
模板輸入變量和引用變量具有各自獨(dú)立的命名空間。let hero中的hero和#hero中的hero并不是同一個(gè)變量。
總結(jié):
<ng-template>在ng中主要通過viewChild TemplateRef ViewContainerRef來實(shí)現(xiàn)結(jié)構(gòu)性操作。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解Angular2表單-模板驅(qū)動的表單(Template-Driven Forms)
- Angular2學(xué)習(xí)教程之TemplateRef和ViewContainerRef詳解
- 詳解Angular 4.x NgTemplateOutlet
- Angular.js 4.x中表單Template-Driven Forms詳解
- Angular.JS中的指令引用template與指令當(dāng)做屬性詳解
- AngularJS ng-template寄宿方式用法分析
- AngularJs Understanding Angular Templates
- AngularJS ng-bind-template 指令詳解
- AngularJS延遲加載html template
相關(guān)文章
JavaScript代碼異常監(jiān)控實(shí)現(xiàn)過程詳解
這篇文章主要介紹了JavaScript代碼異常監(jiān)控實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
原生JavaScript實(shí)現(xiàn)幻燈片效果
這篇文章主要為大家詳細(xì)介紹了原生JavaScript實(shí)現(xiàn)幻燈片效果,文中安裝步驟介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-02-02
解決input輸入框僅支持輸入數(shù)字及兩位小數(shù)點(diǎn)的限制
這篇文章主要為大家介紹了解決input輸入框僅支持輸入數(shù)字及兩位小數(shù)點(diǎn)的限制技巧示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
詳解BootStrap中Affix控件的使用及保持布局的美觀的方法
Affix是BootStrap中的一個(gè)很有用的控件,他能夠監(jiān)視瀏覽器的滾動條的位置并讓你的導(dǎo)航始終都在頁面的可視區(qū)域。本文重點(diǎn)給大家介紹BootStrap中Affix控件的使用及保持布局的美觀的方法,感興趣的朋友一起看看吧2016-07-07
weex slider實(shí)現(xiàn)滑動底部導(dǎo)航功能
這篇文章主要為大家詳細(xì)介紹了weex slider實(shí)現(xiàn)滑動底部導(dǎo)航功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
JavaScript常用代碼書寫規(guī)范的超全面總結(jié)
這篇文章給大家全面總結(jié)了JavaScript常用代碼的書寫規(guī)范,分別利用推薦和不推薦的兩種示例代碼讓大家更能直接的了解書寫規(guī)范,其實(shí)關(guān)于javascript代碼規(guī)范我們應(yīng)該遵循古老的原則:“能做并不意味著應(yīng)該做”,好了,下面我們就來一起看看吧。2016-09-09
JavaScript獲取服務(wù)器端時(shí)間的方法
這篇文章主要介紹了JavaScript獲取服務(wù)器端時(shí)間的方法,非常不錯,具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11

