美文网首页Angular框架专题
Angular中的TemplateRef与ViewContain

Angular中的TemplateRef与ViewContain

作者: 听书先生 | 来源:发表于2021-11-18 23:19 被阅读0次

    在HTML5标准中引入了template模板元素,模板元素的处理方式是浏览器的一种机制,允许在加载页面的时候对模板中的内容不进行渲染,但是开发人员可以通过js进行实例化客户端内容。可以想象为模板就是暂存在页面之后的一块内容,当js对他进行处理了,他便会加载到页面中去。

    Angular中提出的ng-template又是另一种比较有趣的处理方式

    前言.png
    1、HTML5中的模板元素:

    HTML5的写法:

    <template id="temp">
      <span>模板元素中span标签</span>
    </template>
    

    这一段内容一般编译运行后是无法显示在页面中的,如果进行代码的修改

    // 先定义一个容器
    <div  id="container"></div>
    
    <template id="temp">
      <span>模板元素中span标签</span>
    </template>
    
    // 获取容器以及模板元素的内容
    let container = document.querySelector('#container'); 
    let temp = document.querySelector('#temp');
    
    let tempNode = document.importNode(temp.content, true);
    
    //将模板元素的内容追加到容器中去
    container.appendChild(tempNode);
    
    

    这样浏览器即可显示模板元素中的内容

    2、Angular中的模板元素:

    Angular中, 模板元素主要应用在结构指令中,但是需要注意的是,在NG4.X版本之后就不推荐直接写template了,官方文档推荐使用ng-template

    import {Component, TemplateRef, ViewChild, AfterViewInit} from '@angular/core';
    
    @Component({
      selector: 'my-app',
      template: `
        <ng-template #temp>
          <span>ng模板在的span标签的内容</span>
        </ng-template>
      `,
    })
    export class AppComponent {
    
      // 获取模板元素
      @ViewChild('temp') temp!: TemplateRef<any>;
    
      ngAfterViewInit() {
        console.dir(this.temp);
      }
    }
    
    
    打印结果.png img.png

    Angular中ng-template中定义的 模板元素,渲染后会被替换成 comment 元素,其内容被 "container"替换掉了 。此外我们通过 @ViewChild 获取的模板元素,是 TemplateRef 类的实例。

    3、templateRef类部分源码:

    Angular2.x的源码:

    // @angular/core/src/linker/template_ref.d.ts
    // 用于表示内嵌的template模板,能够用于创建内嵌视图(Embedded Views)
    export declare abstract class TemplateRef<C> {
        elementRef: ElementRef;
        abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
    }
    

    Angular8.x的源码

    export abstract class TemplateRef<C> {
      //此嵌入视图的父视图中的锚元素。从该“TemplateRef”创建的嵌入式视图的数据绑定和注入上下文从该位置的上下文继承。通常,新的嵌入视图附着到此位置的视图容器,但在在高级用例中,视图可以附加到不同的容器,同时保持来自原始位置的数据绑定和注入上下文。
      abstract get elementRef(): ElementRef;
    
      // 基于此模板实例化嵌入式视图,并将其附着到视图容器。嵌入视图的数据绑定上下文,如声明的那样在`<ng template>`中使用。返回新的嵌入视图对象。
      abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
    
      static __NG_ELEMENT_ID__:
          () => TemplateRef<any>| null = () => SWITCH_TEMPLATE_REF_FACTORY(TemplateRef, ElementRef)
    }
    
    

    抽象类与普通类的区别是抽象类有包含抽象方法,不能直接实例化抽象类,只能实例化该抽象类的子类。

    • 尝试调取源码中的抽象类方法:
        console.dir(this.temp.createEmbeddedView(null));
    
    在编辑器中可以看到有该方法.png
    ViewRef.png
    可以看到rootNodes中包含了模板元素的内容。

    TemplateRef抽象类中定义的 createEmbeddedView抽象方法,该方法的返回值是EmbeddedViewRef对象,当调用 createEmbeddedView 方法后返回了 ViewRef_ 新的嵌入视图对象。该视图对象的 rootNodes 属性包含了 模板中的内容。TemplateRef 实例对象中的 elementRef 属性封装了我们的 comment 元素。

    4、根据源码的思路,我们也可以尝试模板元素的步骤过程
        // 初始化页面的<!-- container --> 占位
        let elementRef = this.temp.elementRef.nativeElement;
        console.log(elementRef.parentNode);
        const container = elementRef.parentNode
        
        // 创建内嵌视图
        let temp = this.temp.createEmbeddedView(null);
        console.log(temp);
        
        // 获取内嵌视图中的rootNodes数组节点
        temp.rootNodes.forEach(node => {
          container.appendChild(node);//追加node节点到容器中去
        })
    
    遍历rootNodes数组中的节点.png

    总结下来angular内部完成ng-template的过程就是创建内嵌视图(embedded view),再去遍历内嵌视图中的 rootNodes,动态的插入node。

    5、ViewContainerRef的作用
    • ViewContainerRef抽象类的源码:
    export abstract class ViewContainerRef {
    
      abstract get element(): ElementRef;
      abstract get injector(): Injector;
    
      /** @deprecated No replacement */
      abstract get parentInjector(): Injector;
      /**
       * Destroys all views in this container.
       */
      abstract clear(): void;
      abstract get(index: number): ViewRef|null;
      abstract get length(): number; // 内嵌视图插入的位置
    
      //  基于TemplateRef创建内嵌视图,并自动添加到视图容器中,可通过index设置
      abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number):
          EmbeddedViewRef<C>;
     
      // 创建组件视图
      abstract createComponent<C>(
          componentFactory: ComponentFactory<C>, index?: number, injector?: Injector,
          projectableNodes?: any[][], ngModule?: NgModuleRef<any>): ComponentRef<C>;
    
      // 相关的操作方法
      abstract insert(viewRef: ViewRef, index?: number): ViewRef;
    
      abstract move(viewRef: ViewRef, currentIndex: number): ViewRef;
    
      abstract indexOf(viewRef: ViewRef): number;
    
      abstract remove(index?: number): void;
    
      abstract detach(index?: number): ViewRef|null;
    
      /**
       * @internal
       * @nocollapse
       */
      static __NG_ELEMENT_ID__:
          () => ViewContainerRef = () => SWITCH_VIEW_CONTAINER_REF_FACTORY(ViewContainerRef, ElementRef)
    }
    
    

    ViewContainerRef:表示的是一个容器,可以添加视图,通过ViewContainerRef实例,可以基于TemplateRef实例创建内嵌视图,并能指定内嵌视图的插入位置,便于对已有的视图进行管理。

    也就是说可以通过ViewContainerRef实例也就是tempVcRef直接创建一个内嵌视图,把获取到的TemplateRef实例也就是temp插入内嵌视图中。实际上步骤与上述等同。

        console.dir(this.tempVcRef);
        this.tempVcRef.createEmbeddedView(this.temp)
    
    image.png
    6、Angular中的视图类型

    ng中视图的类型存在两类,一种就是现在的这种Embedded Views也就是Template 模板元素还有一种是Host Views也就是Component 组件。

    创建Embedded Views也就是内嵌视图的创建过程:
    TemplateRef抽象类的实例调取内部的createEmbeddedView()方法。
    而Host Views的创建过程:

    constructor(private injector: Injector,
        private r: ComponentFactoryResolver) {
        let factory = this.r.resolveComponentFactory(AppComponent);
        let componentRef = factory.create(injector);
        let view = componentRef.hostView;
    }
    
    

    相关文章

      网友评论

        本文标题:Angular中的TemplateRef与ViewContain

        本文链接:https://www.haomeiwen.com/subject/ctidtrtx.html