在HTML5标准中引入了template模板元素,模板元素的处理方式是浏览器的一种机制,允许在加载页面的时候对模板中的内容不进行渲染,但是开发人员可以通过js进行实例化客户端内容。可以想象为模板就是暂存在页面之后的一块内容,当js对他进行处理了,他便会加载到页面中去。
Angular中提出的ng-template
又是另一种比较有趣的处理方式
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;
}
。
网友评论