詳解Angular 4.x NgIf 的用法
NgIf 指令作用
ngIf 指令用于根據(jù)表達(dá)式的值,在指定位置渲染 then 或 else 模板的內(nèi)容。
- then 模板除非綁定到不同的值,否則默認(rèn)是 ngIf 指令關(guān)聯(lián)的內(nèi)聯(lián)模板。
- else 模板除非綁定對(duì)應(yīng)的值,否則默認(rèn)是 null。
NgIf 指令語(yǔ)法
簡(jiǎn)單形式
<!--語(yǔ)法糖--> <div *ngIf="condition">...</div> <!--Angular 2.x中使用template--> <ng-template [ngIf]="condition"><div>...</div></ng-template>
使用else塊
<div *ngIf="condition; else elseBlock">...</div> <ng-template #elseBlock>...</ng-template>
使用then和else塊
<div *ngIf="condition; then thenBlock else elseBlock"></div> <ng-template #thenBlock>...</ng-template> <ng-template #elseBlock>...</ng-template>
使用as語(yǔ)法
<div *ngIf="condition as value; else elseBlock">{{value}}</div> <ng-template #elseBlock>...</ng-template>
NgIf 使用示例
@Component({ selector: 'ng-if-then-else', template: ` <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button> <button (click)="switchPrimary()">Switch Primary</button> show = {{show}} <br> <div *ngIf="show; then thenBlock; else elseBlock">this is ignored</div> <ng-template #primaryBlock>Primary text to show</ng-template> <ng-template #secondaryBlock>Secondary text to show</ng-template> <ng-template #elseBlock>Alternate text while primary text is hidden</ng-template> ` }) class NgIfThenElse implements OnInit { thenBlock: TemplateRef<any> = null; show: boolean = true; @ViewChild('primaryBlock') primaryBlock: TemplateRef<any> = null; @ViewChild('secondaryBlock') secondaryBlock: TemplateRef<any> = null; switchPrimary() { this.thenBlock = this.thenBlock === this.primaryBlock ? this.secondaryBlock : this.primaryBlock; } ngOnInit() { this.thenBlock = this.primaryBlock; } }
基礎(chǔ)知識(shí)
TemplateRef
TemplateRef 實(shí)例用于表示模板對(duì)象,TemplateRef 抽象類(lèi)的定義如下:
// angular\packages\core\src\linker\template_ref.ts export abstract class TemplateRef<C> { abstract get elementRef(): ElementRef; abstract createEmbeddedView(context: C): EmbeddedViewRef<C>; }
ViewContainerRef
ViewContainerRef 實(shí)例提供了 createEmbeddedView()
方法,該方法接收 TemplateRef
對(duì)象作為參數(shù),并將模板中的內(nèi)容作為容器 (comment 元素) 的兄弟元素,插入到頁(yè)面中。
NgIfContext
NgIfContext 實(shí)例用于表示 NgIf 上下文。
// angular\packages\common\src\directives\ng_if.ts export class NgIfContext { public $implicit: any = null; public ngIf: any = null; }
NgIf 源碼分析
NgIf 指令定義
@Directive({ selector: '[ngIf]' // 屬性選擇器 - <ng-template [ngIf]="condition"> })
NgIf 類(lèi)私有屬性及構(gòu)造函數(shù)
export class NgIf { // 創(chuàng)建NgIfContext上下文 private _context: NgIfContext = new NgIfContext(); // 表示then模板對(duì)象 private _thenTemplateRef: TemplateRef<NgIfContext>|null = null; // 表示else模板對(duì)象 private _elseTemplateRef: TemplateRef<NgIfContext>|null = null; // 表示根據(jù)then模板創(chuàng)建的EmbeddedViewRef視圖 private _thenViewRef: EmbeddedViewRef<NgIfContext>|null = null; // 表示根據(jù)else模板創(chuàng)建的EmbeddedViewRef視圖 private _elseViewRef: EmbeddedViewRef<NgIfContext>|null = null; constructor( private _viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>) { this._thenTemplateRef = templateRef; // then模板的默認(rèn)值為ngIf指令關(guān)聯(lián)的內(nèi)聯(lián)模板 } }
NgIf 類(lèi)輸入屬性
@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; // 清除之前創(chuàng)建的視圖 this._updateView(); } @Input() set ngIfElse(templateRef: TemplateRef<NgIfContext>) { this._elseTemplateRef = templateRef; this._elseViewRef = null; // 清除之前創(chuàng)建的視圖 this._updateView(); }
_updateView() 私有方法
// 更新視圖 private _updateView() { // this._context.$implicit = this._context.ngIf = condition // 若condition表達(dá)式的值為truthy if (this._context.$implicit) { // 若_thenViewRef為null且_thenTemplateRef存在,則創(chuàng)建_thenViewRef內(nèi)嵌視圖 if (!this._thenViewRef) { this._viewContainer.clear(); this._elseViewRef = null; if (this._thenTemplateRef) { this._thenViewRef = this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context); } } } else { // condition表達(dá)式的值為falsy // 若_elseViewRef為null且_elseTemplateRef存在,則創(chuàng)建_elseViewRef內(nèi)嵌視圖 if (!this._elseViewRef) { this._viewContainer.clear(); this._thenViewRef = null; if (this._elseTemplateRef) { this._elseViewRef = this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context); } } } }
ngIf
指令的源碼相對(duì)比較簡(jiǎn)單,最核心的是 _updateView()
方法。而該方法中最重要的功能就是如何基于模板對(duì)象創(chuàng)建內(nèi)嵌視圖。接下來(lái)我們來(lái)分析一下 ViewContainerRef
對(duì)象的 createEmbeddedView()
方法。
ViewContainerRef - createEmbeddedView()
方法簽名
// angular\packages\core\src\linker\view_container_ref.ts export abstract class ViewContainerRef { /** * 基于TemplateRef對(duì)象創(chuàng)建Embedded View(內(nèi)嵌視圖),然后根據(jù)`index`指定的值,插入到容器中。 * 如果沒(méi)有指定`index`的值,新創(chuàng)建的視圖將作為容器中的最后一個(gè)視圖插入。 */ abstract createEmbeddedView<C>( templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C>; }
方法實(shí)現(xiàn)
// angular\packages\core\src\view\refs.ts class ViewContainerRef_ implements ViewContainerData { // ... createEmbeddedView<C>( templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C> { // 調(diào)用TemplateRef對(duì)象createEmbeddedView()方法創(chuàng)建EmbeddedViewRef對(duì)象 const viewRef = templateRef.createEmbeddedView(context || <any>{}); // 根據(jù)指定的index值,插入到視圖容器中 this.insert(viewRef, index); return viewRef; } } // ViewContainerData接口繼承于ViewContainerRef抽象類(lèi) export interface ViewContainerData extends ViewContainerRef { _embeddedViews: ViewData[]; } export interface ViewData { def: ViewDefinition; root: RootData; renderer: Renderer2; parentNodeDef: NodeDef|null; parent: ViewData|null; viewContainerParent: ViewData|null; component: any; context: any; nodes: {[key: number]: NodeData}; state: ViewState; oldValues: any[]; disposables: DisposableFn[]|null; }
通過(guò)觀察 ViewContainerRef_
類(lèi)中的 createEmbeddedView()
方法,我們發(fā)現(xiàn)該方法內(nèi)部是調(diào)用 TemplateRef
對(duì)象的 createEmbeddedView()
方法來(lái)創(chuàng)建內(nèi)嵌視圖。因此接下來(lái)我們?cè)賮?lái)分析一下 TemplateRef
對(duì)象的 createEmbeddedView()
方法。
TemplateRef - createEmbeddedView()
方法簽名
// angular\packages\core\src\linker\template_ref.ts export abstract class TemplateRef<C> { abstract createEmbeddedView(context: C): EmbeddedViewRef<C>; }
方法實(shí)現(xiàn)
// angular\packages\core\src\view\refs.ts class TemplateRef_ extends TemplateRef<any> implements TemplateData { // ... createEmbeddedView(context: any): EmbeddedViewRef<any> { return new ViewRef_(Services.createEmbeddedView( this._parentView, this._def, this._def.element !.template !, context)); } } export interface TemplateData extends TemplateRef<any> { _projectedViews: ViewData[]; }
看完上面的源碼,毫無(wú)疑問(wèn)接下來(lái)我們要繼續(xù)分析 Services
對(duì)象中的 createEmbeddedView()
方法。
Services - createEmbeddedView()
Services 對(duì)象定義
// angular\packages\core\src\view\types.ts export const Services: Services = { setCurrentNode: undefined !, createRootView: undefined !, createEmbeddedView: undefined !, createComponentView: undefined !, createNgModuleRef: undefined !, overrideProvider: undefined !, clearProviderOverrides: undefined !, checkAndUpdateView: undefined !, checkNoChangesView: undefined !, destroyView: undefined !, resolveDep: undefined !, createDebugContext: undefined !, handleEvent: undefined !, updateDirectives: undefined !, updateRenderer: undefined !, dirtyParentQueries: undefined !, };
Services 對(duì)象初始化
// angular\packages\core\src\view\services.ts export function initServicesIfNeeded() { if (initialized) { return; } initialized = true; const services = isDevMode() ? createDebugServices() : createProdServices(); Services.setCurrentNode = services.setCurrentNode; Services.createRootView = services.createRootView; Services.createEmbeddedView = services.createEmbeddedView; Services.createComponentView = services.createComponentView; Services.createNgModuleRef = services.createNgModuleRef; Services.overrideProvider = services.overrideProvider; Services.clearProviderOverrides = services.clearProviderOverrides; Services.checkAndUpdateView = services.checkAndUpdateView; Services.checkNoChangesView = services.checkNoChangesView; Services.destroyView = services.destroyView; Services.resolveDep = resolveDep; Services.createDebugContext = services.createDebugContext; Services.handleEvent = services.handleEvent; Services.updateDirectives = services.updateDirectives; Services.updateRenderer = services.updateRenderer; Services.dirtyParentQueries = dirtyParentQueries; }
在 initServicesIfNeeded()
方法中,會(huì)根據(jù)當(dāng)前所處的模式,創(chuàng)建不同的 Services 對(duì)象。接下來(lái) 我們直接來(lái)看一下 createProdServices()
方法:
function createProdServices() { return { setCurrentNode: () => {}, createRootView: createProdRootView, createEmbeddedView: createEmbeddedView // 省略了其它方法 }
createEmbeddedView() 方法
// angular\packages\core\src\view\view.ts export function createEmbeddedView( parent: ViewData, anchorDef: NodeDef, viewDef: ViewDefinition, context?: any): ViewData { // embedded views are seen as siblings to the anchor, so we need // to get the parent of the anchor and use it as parentIndex. // 創(chuàng)建ViewData對(duì)象 const view = createView(parent.root, parent.renderer, parent, anchorDef, viewDef); // 初始化ViewData對(duì)象-設(shè)置component及context屬性的值 initView(view, parent.component, context); // 創(chuàng)建視圖中的節(jié)點(diǎn),即設(shè)置view.nodes數(shù)組的屬性值 // const nodes = view.nodes; for(...) { ...; nodes[i] = nodeData; } createViewNodes(view); return view; }
此時(shí)發(fā)現(xiàn)如果完整分析所有的方法,會(huì)涉及太多的內(nèi)容。源碼分析就到此結(jié)束,有興趣的讀者請(qǐng)自行閱讀源碼哈(請(qǐng)各位讀者見(jiàn)諒)。接下來(lái)我們來(lái)總結(jié)一下 createEmbeddedView()
方法調(diào)用流程:
ViewContainerRef_ -> createEmbeddedView() => TemplateRef_ -> createEmbeddedView() => Services -> createEmbeddedView() => Call createEmbeddedView()
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用xampp將angular項(xiàng)目運(yùn)行在web服務(wù)器的教程
這篇文章主要介紹了使用xampp將angular項(xiàng)目運(yùn)行在web服務(wù)器,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09div實(shí)現(xiàn)自適應(yīng)高度的textarea實(shí)現(xiàn)angular雙向綁定
本文主要介紹了div實(shí)現(xiàn)自適應(yīng)高度的textarea,實(shí)現(xiàn)angular雙向綁定的方法。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-01-01走進(jìn)AngularJs之過(guò)濾器(filter)詳解
本篇文章主要介紹了AngularJs之過(guò)濾器(filter)詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02angular6?Error:Debug?Failure?at?typeToString解決分析
這篇文章主要為大家介紹了angular6?Error:Debug?Failure?at?typeToString解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11AngularJS基礎(chǔ) ng-selected 指令簡(jiǎn)單示例
本文主要介紹AngularJS ng-selected 指令,這里對(duì)ng-selected 指令的基礎(chǔ)資料做了詳細(xì)介紹,并附有示例代碼,有需要的小伙伴可以參考下2016-08-08利用Angular.js限制textarea輸入的字?jǐn)?shù)
相信在大家已經(jīng)學(xué)習(xí)了足夠多關(guān)于AngularJS的知識(shí)后,就可以開(kāi)始創(chuàng)建第一個(gè)AngularJS應(yīng)用程序,這篇文章通過(guò)示例給大家介紹如何利用Angular.js限制textarea輸入的字?jǐn)?shù),有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2016-10-10