详解Angular 4.x NgIf 的用法
NgIf指令作用
ngIf指令用于根据表达式的值,在指定位置渲染then或else模板的内容。
- then模板除非绑定到不同的值,否则默认是ngIf指令关联的内联模板。
- else模板除非绑定对应的值,否则默认是null。
NgIf指令语法
简单形式
... ...
使用else块
......
使用then和else块
... ...
使用as语法
{{value}}...
NgIf使用示例
@Component({ selector:'ng-if-then-else', template:` show={{show}}
thisisignoredPrimarytexttoshow Secondarytexttoshow Alternatetextwhileprimarytextishidden ` }) classNgIfThenElseimplementsOnInit{ thenBlock:TemplateRef=null; show:boolean=true; @ViewChild('primaryBlock') primaryBlock:TemplateRef =null; @ViewChild('secondaryBlock') secondaryBlock:TemplateRef =null; switchPrimary(){ this.thenBlock=this.thenBlock===this.primaryBlock? this.secondaryBlock:this.primaryBlock; } ngOnInit(){ this.thenBlock=this.primaryBlock; } }
基础知识
TemplateRef
TemplateRef实例用于表示模板对象,TemplateRef抽象类的定义如下:
//angular\packages\core\src\linker\template_ref.ts exportabstractclassTemplateRef{ abstractgetelementRef():ElementRef; abstractcreateEmbeddedView(context:C):EmbeddedViewRef ; }
ViewContainerRef
ViewContainerRef实例提供了createEmbeddedView()方法,该方法接收TemplateRef对象作为参数,并将模板中的内容作为容器(comment元素)的兄弟元素,插入到页面中。
NgIfContext
NgIfContext实例用于表示NgIf上下文。
//angular\packages\common\src\directives\ng_if.ts exportclassNgIfContext{ public$implicit:any=null; publicngIf:any=null; }
NgIf源码分析
NgIf指令定义
@Directive({ selector:'[ngIf]'//属性选择器-})
NgIf类私有属性及构造函数
exportclassNgIf{ //创建NgIfContext上下文 private_context:NgIfContext=newNgIfContext(); //表示then模板对象 private_thenTemplateRef:TemplateRef|null=null; //表示else模板对象 private_elseTemplateRef:TemplateRef |null=null; //表示根据then模板创建的EmbeddedViewRef视图 private_thenViewRef:EmbeddedViewRef |null=null; //表示根据else模板创建的EmbeddedViewRef视图 private_elseViewRef:EmbeddedViewRef |null=null; constructor( private_viewContainer:ViewContainerRef, templateRef:TemplateRef ){ this._thenTemplateRef=templateRef;//then模板的默认值为ngIf指令关联的内联模板 } }
NgIf类输入属性
@Input() setngIf(condition:any){ this._context.$implicit=this._context.ngIf=condition; this._updateView();//更新视图 } @Input() setngIfThen(templateRef:TemplateRef){ this._thenTemplateRef=templateRef; this._thenViewRef=null;//清除之前创建的视图 this._updateView(); } @Input() setngIfElse(templateRef:TemplateRef ){ this._elseTemplateRef=templateRef; this._elseViewRef=null;//清除之前创建的视图 this._updateView(); }
_updateView()私有方法
//更新视图 private_updateView(){ //this._context.$implicit=this._context.ngIf=condition //若condition表达式的值为truthy if(this._context.$implicit){ //若_thenViewRef为null且_thenTemplateRef存在,则创建_thenViewRef内嵌视图 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存在,则创建_elseViewRef内嵌视图 if(!this._elseViewRef){ this._viewContainer.clear(); this._thenViewRef=null; if(this._elseTemplateRef){ this._elseViewRef= this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context); } } } }
ngIf指令的源码相对比较简单,最核心的是_updateView()方法。而该方法中最重要的功能就是如何基于模板对象创建内嵌视图。接下来我们来分析一下ViewContainerRef对象的createEmbeddedView()方法。
ViewContainerRef-createEmbeddedView()
方法签名
//angular\packages\core\src\linker\view_container_ref.ts exportabstractclassViewContainerRef{ /** *基于TemplateRef对象创建EmbeddedView(内嵌视图),然后根据`index`指定的值,插入到容器中。 *如果没有指定`index`的值,新创建的视图将作为容器中的最后一个视图插入。 */ abstractcreateEmbeddedView( templateRef:TemplateRef , context?:C,index?:number): EmbeddedViewRef ; }
方法实现
//angular\packages\core\src\view\refs.ts classViewContainerRef_implementsViewContainerData{ //... createEmbeddedView( templateRef:TemplateRef , context?:C,index?:number): EmbeddedViewRef { //调用TemplateRef对象createEmbeddedView()方法创建EmbeddedViewRef对象 constviewRef=templateRef.createEmbeddedView(context|| {}); //根据指定的index值,插入到视图容器中 this.insert(viewRef,index); returnviewRef; } } //ViewContainerData接口继承于ViewContainerRef抽象类 exportinterfaceViewContainerDataextendsViewContainerRef{ _embeddedViews:ViewData[]; } exportinterfaceViewData{ 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()方法,我们发现该方法内部是调用TemplateRef对象的createEmbeddedView()方法来创建内嵌视图。因此接下来我们再来分析一下TemplateRef对象的createEmbeddedView()方法。
TemplateRef-createEmbeddedView()
方法签名
//angular\packages\core\src\linker\template_ref.ts exportabstractclassTemplateRef{ abstractcreateEmbeddedView(context:C):EmbeddedViewRef ; }
方法实现
//angular\packages\core\src\view\refs.ts classTemplateRef_extendsTemplateRefimplementsTemplateData{ //... createEmbeddedView(context:any):EmbeddedViewRef { returnnewViewRef_(Services.createEmbeddedView( this._parentView,this._def,this._def.element!.template!,context)); } } exportinterfaceTemplateDataextendsTemplateRef { _projectedViews:ViewData[]; }
看完上面的源码,毫无疑问接下来我们要继续分析Services对象中的createEmbeddedView()方法。
Services-createEmbeddedView()
Services对象定义
//angular\packages\core\src\view\types.ts exportconstServices: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 exportfunctioninitServicesIfNeeded(){ if(initialized){ return; } initialized=true; constservices=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()方法中,会根据当前所处的模式,创建不同的Services对象。接下来我们直接来看一下createProdServices()方法:
functioncreateProdServices(){ return{ setCurrentNode:()=>{}, createRootView:createProdRootView, createEmbeddedView:createEmbeddedView//省略了其它方法 }
createEmbeddedView()方法
//angular\packages\core\src\view\view.ts exportfunctioncreateEmbeddedView( parent:ViewData,anchorDef:NodeDef,viewDef:ViewDefinition,context?:any):ViewData{ //embeddedviewsareseenassiblingstotheanchor,soweneed //togettheparentoftheanchoranduseitasparentIndex. //创建ViewData对象 constview=createView(parent.root,parent.renderer,parent,anchorDef,viewDef); //初始化ViewData对象-设置component及context属性的值 initView(view,parent.component,context); //创建视图中的节点,即设置view.nodes数组的属性值 //constnodes=view.nodes;for(...){...;nodes[i]=nodeData;} createViewNodes(view); returnview; }
此时发现如果完整分析所有的方法,会涉及太多的内容。源码分析就到此结束,有兴趣的读者请自行阅读源码哈(请各位读者见谅)。接下来我们来总结一下createEmbeddedView()方法调用流程:
ViewContainerRef_->createEmbeddedView() =>TemplateRef_->createEmbeddedView() =>Services->createEmbeddedView() =>CallcreateEmbeddedView()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。