详解Angular 4.x NgIf 的用法

网友投稿 693 2023-05-13


详解Angular 4.x NgIf 的用法

NgIf 指令作用

ngIf 指令用于根据表达式的值,在指定位置渲染 then 或 else 模板的内容。

then 模板除非绑定到不同的值,否则默认是 ngIf 指令关联的内联模板。

else 模板除非绑定对应的值,否则默认是 null。

NgIf 指令语法

简单形式

...

使用else块

...

使用then和else块

...

...

使用as语法

...

NgIf 使用示例

@Component({

selector: 'ng-if-then-else',

template: `

show = {{show}}


Primary text to show

Secondary text to show

Alternate text while primary text is hidden

`

})

class NgIfThenElse implements OnInit {

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.primaryBlohttp://ck;

}

ngOnInit() {

this.thenBlock = this.primaryBlock;

}

}

基础知识

TemplateRef

TemplateRef 实例用于表示模板对象,TemplateRef 抽象类的定义如下:

// angular\packages\core\src\linker\template_ref.ts

export abstract class TemplateRef {

abstract get elementRef(): ElementRef;

abstract createEmbeddedView(context: C): EmbeddedViewRef;

}

ViewContainerRef

ViewContainerRef 实例提供了 createEmbeddedView() 方法,该方法接收 TemplateRef 对象作为参数,并将模板中的内容作为容器 (comment 元素) 的兄弟元素,插入到页面中。

NgIfContext

NgIfContext 实例用于表示 NgIf 上下文。

// angular\packages\common\src\directives\ng_ifupFnlX.ts

export class NgIfContext {

public $implicit: any = null;

public ngIf: any = null;

}

NgIf 源码分析

NgIf 指令定义

@Directive({

selector: '[ngIf]' // 属性选择器 -

})

NgIf 类私有属性及构造函数

export class NgIf {

// 创建NgIfContext上下文

private _context: NgIfContext = new NgIfContext();

// 表示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()

set ngIf(condition: any) {

this._context.$implicit = this._context.ngIf = condition;

this._updateView(); // 更新视图

}

@Input()

set ngIfThen(templateRef: TemplateRef) {

this._thenTemplateRef = templateRef;

this._thenViewRef = null; // 清除之前创建的视图

this._updateView();

}

@Input()

set ngIfElse(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,

upFnlX 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

export abstract class ViewContainerRef {

/**

* 基于TemplateRef对象创建Embedded View(内嵌视图),然后根据`index`指定的值,插入到容器中。

* 如果没有指定`index`的值,新创建的视图将作为容器中的最后一个视图插入。

*/

abstract createEmbeddedView(

templateRef: TemplateRef,

context?: C, index?: number):

EmbeddedViewRef;

}

方法实现

// angular\packages\core\src\view\refs.ts

class ViewContainerRef_ implements ViewContainerData {

// ...

createEmbeddedView(

templateRef: TemplateRef,

context?: C, index?: number):

EmbeddedViewRef {

// 调用TemplateRef对象createEmbeddedView()方法创建EmbeddedViewRef对象

const viewRef = templateRef.createEmbeddedView(context || {});

// 根据指定的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() 方法,我们发现该方法内部是调用 TemplateRef 对象的 createEmbeddedView() 方法来创建内嵌视图。因此接下来我们再来分析一下 TemplateRef 对象的 createEmbeddedView() 方法。

TemplateRef - createEmbeddedView()

方法签名

// angular\packages\core\src\linker\template_ref.ts

export abstract class TemplateRef {

abstract createEmbeddedView(context: C): EmbeddedViewRef;

}

方法实现

// angular\packages\core\src\view\refs.ts

class TemplateRef_ extends TemplateRef implements TemplateData {

// ...

createEmbeddedView(context: any): EmbeddedViewRef {

return new ViewRef_(Services.createEmbeddedView(

this._parentView, this._def, this._def.element !.template !, context));

}

}

export interface TemplateData extends TemplateRef {

_projectedViews: ViewData[];

}

看完上面的源码,毫无疑问接下来我们要继续分析 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 !,

updateRendereupFnlXr: 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() 方法中,会根据当前所处的模式,创建不同的 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.

// 创建ViewData对象

const view = createView(parent.root, parent.renderer, parent, anchorDef, viewDef);

// 初始化ViewData对象-设置component及context属性的值

initView(view, parent.component, context);

// 创建视图中的节点,即设置view.nodes数组的属性值

// const nodes = view.nodes; for(...) { ...; nodes[i] = nodeData; }

createViewNodes(view);

return view;

}

此时发现如果完整分析所有的方法,会涉及太多的内容。源码分析就到此结束,有兴趣的读者请自行阅读源码哈(请各位读者见谅)。接下来我们来总结一下 createEmbeddedView() 方法调用流程:

ViewContainerRef_ -> createEmbeddedView()

=> TemplateRef_ -> createEmbeddedView()

=> Services -> createEmbeddedView()

=> Call createEmbeddedView()


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java网络通信基础编程(必看篇)
下一篇:实现接口方法(实现接口方法的修饰符有哪些)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~