Angular2学习教程之TemplateRef和ViewContainerRef详解
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。
好了,以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。