当前位置: 移动技术网 > IT编程>脚本编程>AngularJs > Angular2学习教程之TemplateRef和ViewContainerRef详解

Angular2学习教程之TemplateRef和ViewContainerRef详解

2018年04月26日  | 移动技术网IT编程  | 我要评论

templateref

在介绍 templateref 前,我们先来了解一下 html 模板元素 - <template> 。模板元素是一种机制,允许包含加载页面时不渲染,但又可以随后通过 javascript 进行实例化的客户端内容。我们可以将模板视作为存储在页面上稍后使用的一小段内容。

在 html5 标准引入 template 模板元素之前,我们都是使用 <script> 标签进行客户端模板的定义,具体如下:

<script id="tpl-mock" type="text/template">
 <span>i am span in mock template</span>
</script>

对于支持 html5 template 模板元素的浏览器,我们可以这样创建客户端模板:

<template id="tpl">
 <span>i am span in template</span>
</template>

下面我们来看一下 html5 template 模板元素的使用示例:

<!doctype html>
<html lang="en">
<head><meta charset="utf-8"> <title>html5 template element demo</title></head>
<body>
<h4>html5 template element demo</h4>
<!-- template container -->
<div class="tpl-container"></div>
<!-- template -->
<template id="tpl">
 <span>i am span in template</span>
</template>
<!-- script -->
<script type="text/javascript">
 (function rendertpl() {
 if ('content' in document.createelement('template')) {
 var tpl = document.queryselector('#tpl');
 var tplcontainer = document.queryselector('.tpl-container');
 var tplnode = document.importnode(tpl.content, true);
 tplcontainer.appendchild(tplnode); 
 } else {
 throw new error("current browser doesn't support template element");
 }
 })();
</script>
</body>
</html>

以上代码运行后,在浏览器中我们会看到以下内容:

html5 template element demo

i am span in template

而当我们注释掉 tplcontainer.appendchild(tplnode) 语句时,刷新浏览器后看到的是:

html5 template element demo

这说明页面中 <template> 模板元素中的内容,如果没有进行处理对用户来说是不可见的。angular 2 中,<template> 模板元素主要应用在结构指令中,接下来我们先来介绍一下本文中的第一个主角 - templateref:

import {component, templateref, viewchild, afterviewinit} from '@angular/core';

@component({
 selector: 'my-app',
 template: `
 <h1>welcome to angular world</h1>
 <template #tpl>
 <span>i am span in template</span>
 </template>
 `,
})
export class appcomponent {
 name: string = 'semlinker';

 @viewchild('tpl')
 tpl: templateref<any>;

 ngafterviewinit() {
 console.dir(this.tpl);
 }
}

上述代码运行后的控制台的输出结果如下:

从上图中,我们发现 @component template 中定义的 <template> 模板元素,渲染后被替换成 comment 元素,其内容为 "template bindings={}" 。此外我们通过 @viewchild 获取的模板元素,是 templateref_ 类的实例,接下来我们来研究一下 templateref_ 类:

templateref_

// @angular/core/src/linker/template_ref.d.ts
export declare class templateref_<c> extends templateref<c> {
 private _parentview;
 private _nodeindex;
 private _nativeelement;
 constructor(_parentview: appview<any>, _nodeindex: number, _nativeelement: any);
 createembeddedview(context: c): embeddedviewref<c>;
 elementref: elementref;
}

templateref

// @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>;
}

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

我们已经知道 <template> 模板元素,渲染后被替换成 comment 元素,那么应该如何显示我们模板中定义的内容呢 ?我们注意到了 templateref 抽象类中定义的 createembeddedview抽象方法,该方法的返回值是 embeddedviewref 对象。那好我们马上来试一下:

import {component, templateref, viewchild, afterviewinit} from '@angular/core';

@component({
 selector: 'my-app',
 template: `
 <h1>welcome to angular world</h1>
 <template #tpl>
 <span>i am span in template</span>
 </template>
 `,
})
export class appcomponent {
 name: string = 'semlinker';

 @viewchild('tpl')
 tpl: templateref<any>;

 ngafterviewinit() {
 let embeddedview = this.tpl.createembeddedview(null);
 console.dir(embeddedview);
 }
}

上述代码运行后的控制台的输出结果如下:

从图中我们可以知道,当调用 createembeddedview 方法后返回了 viewref_ 视图对象。该视图对象的 rootnodes 属性包含了 <template> 模板中的内容。在上面的例子中,我们知道了 templateref 实例对象中的 elementref 属性封装了我们的 comment 元素,那么我们可以通过 insertbefore 方法来创建我们模板中定义的内容。

import { component, templateref, viewchild, afterviewinit } from '@angular/core';

@component({
 selector: 'my-app',
 template: `
 <h1>welcome to angular world</h1>
 <template #tpl>
 <span>i am span in template {{name}}</span>
 </template>
 `,
})
export class appcomponent {
 name: string = 'semlinker';

 @viewchild('tpl')
 tpl: templateref<any>;

 ngafterviewinit() {
 // 页面中的<!--template bindings={}-->元素
 let commentelement = this.tpl.elementref.nativeelement;
 // 创建内嵌视图
 let embeddedview = this.tpl.createembeddedview(null);
 // 动态添加子节点
 embeddedview.rootnodes.foreach((node) => {
 commentelement.parentnode
  .insertbefore(node, commentelement.nextsibling);
 });
 }
}

成功运行上面的代码后,在浏览器中我们会看到以下内容:

welcome to angular world

i am span in template

现在我们来回顾一下,上面的处理步骤:

  • 创建内嵌视图(embedded view)
  • 遍历内嵌视图中的 rootnodes,动态的插入 node

虽然我们已经成功的显示出 template 模板元素中的内容,但发现整个流程还是太复杂了,那有没有简单地方式呢 ?是时候介绍本文中第二个主角 - viewcontainerref。

viewcontainerref

我们先来检验一下它的能力,然后再来好好地分析它。具体示例如下:

import { component, templateref, viewchild, viewcontainerref, afterviewinit } from '@angular/core';

@component({
 selector: 'my-app',
 template: `
 <h1>welcome to angular world</h1>
 <template #tpl>
  <span>i am span in template</span>
 </template>
 `,
})
export class appcomponent {
 name: string = 'semlinker';

 @viewchild('tpl')
 tplref: templateref<any>;

 @viewchild('tpl', { read: viewcontainerref })
 tplvcref: viewcontainerref;

 ngafterviewinit() {
 // console.dir(this.tplvcref); (1)
 this.tplvcref.createembeddedview(this.tplref);
 }
}

移除上面代码中的注释,即可在控制台看到以下的输出信息:

而在浏览器中我们会看到以下内容:

welcome to angular world

i am span in template

接下来我们来看一下 viewcontainerref_ 类:

// @angular/core/src/linker/view_container_ref.d.ts
// 用于表示一个视图容器,可添加一个或多个视图
export declare class viewcontainerref_ implements viewcontainerref {
 ...
 length: number; // 返回视图容器中已存在的视图个数
 element: elementref;
 injector: injector;
 parentinjector: injector;
  // 基于templateref创建内嵌视图,并自动添加到视图容器中,可通过index设置
 // 视图添加的位置
 createembeddedview<c>(templateref: templateref<c>, context?: c, 
  index?: number): embeddedviewref<c>;
 // 基 componentfactory创建组件视图
 createcomponent<c>(componentfactory: componentfactory<c>,
  index?: number, injector?: injector, projectablenodes?: any[][]): componentref<c>;
 insert(viewref: viewref, index?: number): viewref;
 move(viewref: viewref, currentindex: number): viewref;
 indexof(viewref: viewref): number;
 remove(index?: number): void;
 detach(index?: number): viewref;
 clear(): void;
}

通过源码我们可以知道通过 viewcontainerref_ 实例,我们可以方便地操作视图,也可以方便地基于 templateref 创建视图。现在我们来总结一下 templateref 与 viewcontainerref。

templateref:用于表示内嵌的 template 模板元素,通过 templateref 实例,我们可以方便创建内嵌视图(embedded views),且可以轻松地访问到通过 elementref 封装后的 nativeelement。需要注意的是组件视图中的 template 模板元素,经过渲染后会被替换成 comment 元素。

viewcontainerref:用于表示一个视图容器,可添加一个或多个视图。通过 viewcontainerref 实例,我们可以基于 templateref 实例创建内嵌视图,并能指定内嵌视图的插入位置,也可以方便对视图容器中已有的视图进行管理。简而言之,viewcontainerref 的主要作用是创建和管理内嵌视图或组件视图。

我有话说

1.angular 2 支持的 view(视图) 类型有哪几种 ?

  • embedded views - template 模板元素
  • host views - component 组件

1.1 如何创建 embedded view

ngafterviewinit() {
 let view = this.tpl.createembeddedview(null);
}

1.2 如何创建 host view

constructor(private injector: injector,
 private r: componentfactoryresolver) {
 let factory = this.r.resolvecomponentfactory(appcomponent);
 let componentref = factory.create(injector);
 let view = componentref.hostview;
}

2.angular 2 component 组件中定义的 <template> 模板元素为什么渲染后会被移除 ?

因为 <template> 模板元素,已经被 angular 2 解析并封装成 templateref 实例,通过 templateref 实例,我们可以方便地创建内嵌视图(embedded view),我们不需要像开篇中的例子那样,手动操作 <template> 模板元素。

3.viewref 与 embeddedviewref 之间有什么关系 ?

viewref 用于表示 angular view(视图),视图是可视化的 ui 界面。embeddedviewref 继承于 viewref,用于表示 <template> 模板元素中定义的 ui 元素。

viewref

// @angular/core/src/linker/view_ref.d.ts
export declare abstract class viewref {
 destroyed: boolean;
 abstract ondestroy(callback: function): any;
}

embeddedviewref

// @angular/core/src/linker/view_ref.d.ts
export declare abstract class embeddedviewref<c> extends viewref {
 context: c;
 rootnodes: any[]; // 保存<template>模板中定义的元素
 abstract destroy(): void; // 用于销毁视图
}

总结

angular 2 中 templateref 与 viewcontainerref 的概念对于初学者来说会比较羞涩难懂,本文从基本的 html 5 <template> 模板元素开始,介绍了如何操作和应用页面中定义的模板。然后通过实例介绍了 angular 2 中 templateref 和 viewcontainerref 的定义和作用。希望通过这篇文章,读者能更好的理解 templateref 与 viewcontainerref。

好了,以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网