详解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}}
thisisignored
Primarytexttoshow
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()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。