詳解Angular 4.x NgIf 的用法
NgIf 指令作用
ngIf 指令用于根據(jù)表達式的值,在指定位置渲染 then 或 else 模板的內(nèi)容。
- then 模板除非綁定到不同的值,否則默認是 ngIf 指令關(guān)聯(lián)的內(nèi)聯(lián)模板。
- else 模板除非綁定對應(yīng)的值,否則默認是 null。
NgIf 指令語法
簡單形式
<!--語法糖--> <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語法
<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ǔ)知識
TemplateRef
TemplateRef 實例用于表示模板對象,TemplateRef 抽象類的定義如下:
// 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 實例提供了 createEmbeddedView() 方法,該方法接收 TemplateRef 對象作為參數(shù),并將模板中的內(nèi)容作為容器 (comment 元素) 的兄弟元素,插入到頁面中。
NgIfContext
NgIfContext 實例用于表示 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 類私有屬性及構(gòu)造函數(shù)
export class NgIf {
// 創(chuàng)建NgIfContext上下文
private _context: NgIfContext = new NgIfContext();
// 表示then模板對象
private _thenTemplateRef: TemplateRef<NgIfContext>|null = null;
// 表示else模板對象
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模板的默認值為ngIf指令關(guān)聯(lián)的內(nèi)聯(lián)模板
}
}
NgIf 類輸入屬性
@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表達式的值為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表達式的值為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 指令的源碼相對比較簡單,最核心的是 _updateView() 方法。而該方法中最重要的功能就是如何基于模板對象創(chuàng)建內(nèi)嵌視圖。接下來我們來分析一下 ViewContainerRef 對象的 createEmbeddedView() 方法。
ViewContainerRef - createEmbeddedView()
方法簽名
// angular\packages\core\src\linker\view_container_ref.ts
export abstract class ViewContainerRef {
/**
* 基于TemplateRef對象創(chuàng)建Embedded View(內(nèi)嵌視圖),然后根據(jù)`index`指定的值,插入到容器中。
* 如果沒有指定`index`的值,新創(chuàng)建的視圖將作為容器中的最后一個視圖插入。
*/
abstract createEmbeddedView<C>(
templateRef: TemplateRef<C>,
context?: C, index?: number):
EmbeddedViewRef<C>;
}
方法實現(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對象createEmbeddedView()方法創(chuàng)建EmbeddedViewRef對象
const viewRef = templateRef.createEmbeddedView(context || <any>{});
// 根據(jù)指定的index值,插入到視圖容器中
this.insert(viewRef, index);
return viewRef;
}
}
// ViewContainerData接口繼承于ViewContainerRef抽象類
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;
}
通過觀察 ViewContainerRef_ 類中的 createEmbeddedView() 方法,我們發(fā)現(xiàn)該方法內(nèi)部是調(diào)用 TemplateRef 對象的 createEmbeddedView() 方法來創(chuàng)建內(nèi)嵌視圖。因此接下來我們再來分析一下 TemplateRef 對象的 createEmbeddedView() 方法。
TemplateRef - createEmbeddedView()
方法簽名
// angular\packages\core\src\linker\template_ref.ts
export abstract class TemplateRef<C> {
abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
}
方法實現(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[];
}
看完上面的源碼,毫無疑問接下來我們要繼續(xù)分析 Services 對象中的 createEmbeddedView() 方法。
Services - createEmbeddedView()
Services 對象定義
// 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 對象初始化
// 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() 方法中,會根據(jù)當(dāng)前所處的模式,創(chuàng)建不同的 Services 對象。接下來 我們直接來看一下 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對象
const view = createView(parent.root, parent.renderer, parent, anchorDef, viewDef);
// 初始化ViewData對象-設(shè)置component及context屬性的值
initView(view, parent.component, context);
// 創(chuàng)建視圖中的節(jié)點,即設(shè)置view.nodes數(shù)組的屬性值
// const nodes = view.nodes; for(...) { ...; nodes[i] = nodeData; }
createViewNodes(view);
return view;
}
此時發(fā)現(xiàn)如果完整分析所有的方法,會涉及太多的內(nèi)容。源碼分析就到此結(jié)束,有興趣的讀者請自行閱讀源碼哈(請各位讀者見諒)。接下來我們來總結(jié)一下 createEmbeddedView() 方法調(diào)用流程:
ViewContainerRef_ -> createEmbeddedView() => TemplateRef_ -> createEmbeddedView() => Services -> createEmbeddedView() => Call createEmbeddedView()
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Angular.js數(shù)據(jù)綁定時自動轉(zhuǎn)義html標(biāo)簽及內(nèi)容
本篇文章主要介紹了詳解Angular.js數(shù)據(jù)綁定時自動轉(zhuǎn)義html標(biāo)簽及內(nèi)容 ,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-03-03
AngularJS基礎(chǔ) ng-mouseenter 指令示例代碼
本文主要介紹AngularJS ng-mouseenter 指令,這里對ng-mouseenter 指令基礎(chǔ)資料做了詳細整理,并附代碼實例,有需要的小伙伴可以參考下2016-08-08
Angular.js中$apply()和$digest()的深入理解
相信大家都知道$digest()和$apply()是AngularJS中的兩個核心并且有時候容易引人誤解的部分。我們需要深入理解這兩者是如何運作的,從而才能理解AngularJS本身是如何運作的。本文的目的就是介紹$digest()和$apply()是如何確確實實的對你有用的。下面來一起看看吧。2016-10-10
AngularJS中update兩次出現(xiàn)$promise屬性無法識別的解決方法
最近在工作中用AngularJS中update了兩次之后發(fā)現(xiàn)$promise屬性無法識別了,后來通過查找相關(guān)的資料終于解決了,想著記錄下方便自己或者有需要的朋友,所以本文主要介紹了AngularJS中update兩次出現(xiàn)了$promise屬性無法識別的解決方法,需要的朋友可以參考借鑒。2017-01-01
詳解Angular數(shù)據(jù)綁定及其實現(xiàn)方式
數(shù)據(jù)綁定是將應(yīng)用程序UI或用戶界面綁定到模型的機制。使用數(shù)據(jù)綁定,用戶將能夠使用瀏覽器來操縱網(wǎng)站上存在的元素。2021-05-05
淺談angular4 ng-content 中隱藏的內(nèi)容
本篇文章主要介紹了淺談angular4 ng-content 中隱藏的內(nèi)容,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
Angular.JS中指令ng-if、ng-show/ng-hide和ng-switch的使用教程
在Angular的原生指令中有這幾個指令用來控制元素的展示與否,ng-show/ng-hide/ng-if和ng-switch。在angular性能優(yōu)化中,我們也常常會用到它。這篇文章主要給大家介紹了在Angular.JS中指令ng-if、ng-show/ng-hide和ng-switch的使用教程,需要的朋友可以參考。2017-05-05

