angular之ng-template模板加载


Posted in Javascript onNovember 09, 2017

本文介绍了angular之ng-template模板加载,分享给大家,具体如下:

html5中的template

template标签的含义:HTML <template>元素是一种用于保存客户端内容的机制,该内容在页面加载时是不可见的,但可以在运行时使用JavaScript进行实例化,可以将一个模板视为正在被存储以供随后在文档中使用的一个内容片段。

angular之ng-template模板加载

属性

此元素仅包含全局属性和只读的 content 属性,通过content 可以读取模板内容,而且可以通过判断 content 属性是否存在来判断浏览器是否支持 <template> 元素。

示例

html

<table id="producttable">
 <thead>
  <tr>
   <td>UPC_Code</td>
   <td>Product_Name</td>
  </tr>
 </thead>
 <tbody>
  <!-- 现有数据可以可选地包括在这里 -->
 </tbody>
</table>

<template id="productrow">
 <tr>
  <td class="record"></td>
  <td></td>
 </tr>
</template>

js

// 通过检查来测试浏览器是否支持HTML模板元素 
// 用于保存模板元素的内容属性。
if ('content' in document.createElement('template')) {

 // 使用现有的HTML tbody实例化表和该行与模板
 let t = document.querySelector('#productrow'),
 td = t.content.querySelectorAll("td");
 td[0].textContent = "1235646565";
 td[1].textContent = "Stuff";

 // 克隆新行并将其插入表中
 let tb = document.getElementsByTagName("tbody");
 let clone = document.importNode(t.content, true);
 tb[0].appendChild(clone);
 
 // 创建一个新行
 td[0].textContent = "0384928528";
 td[1].textContent = "Acme Kidney Beans";

 // 克隆新行并将其插入表中
 let clone2 = document.importNode(t.content, true);
 tb[0].appendChild(clone2);

} else {
 // 找到另一种方法来添加行到表,因为不支持HTML模板元素。
}

代码运行后,结果将是一个包含(由 JavaScript 生成)两个新行的 HTML 表格:

UPC_Code  Product_Name
1235646565 Stuff
0384928528 Acme Kidney Beans

注释掉 tb[0].appendChild(clone);和tb[0].appendChild(clone2);,运行代码,只会看到:
UPC_Code Product_Name

说明template元素中的内容如果不经过处理,浏览器是不会渲染的。

angular中的ng-template

<ng-template>是一个 Angular 元素,它永远不会直接显示出来。在渲染视图之前,Angular 会把<ng-template>及其内容替换为一个注释。

以ngIf为例:

angular之ng-template模板加载

<ng-template> 模板元素与html5的template元素一样,需要被特殊处理后才能渲染。ng主要是通过类TemplateRef和ViewContainerRef实现的。

通过阅读ngIf源码学习如何运用<ng-template>

在使用ngIf 指令时我们并未发现ng-template的身影,这是因为"*"(星号)语法糖的原因,这个简写方法是一个微语法,而不是通常的模板表达式, Angular会解开这个语法糖,变成一个<ng-template>标记,包裹着宿主元素及其子元素。

<div *ngIf="hero" >{{hero.name}}</div>

会被解析为

<ng-template [ngIf]="hero">
 <div>{{hero.name}}</div>
</ng-template>`

看下ngIf源码

import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '@angular/core';
@Directive({selector: '[ngIf]'})
export class NgIf {
 private _context: NgIfContext = new NgIfContext();
 private _thenTemplateRef: TemplateRef<NgIfContext>|null = null;
 private _elseTemplateRef: TemplateRef<NgIfContext>|null = null;
 private _thenViewRef: EmbeddedViewRef<NgIfContext>|null = null;
 private _elseViewRef: EmbeddedViewRef<NgIfContext>|null = null;

 constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>) {
  this._thenTemplateRef = templateRef;
 }

 @Input()
 set ngIf(condition: any) {
  this._context.$implicit = this._context.ngIf = condition;
  this._updateView();
 }

 @Input()
 set ngIfThen(templateRef: TemplateRef<NgIfContext>) {
  this._thenTemplateRef = templateRef;
  this._thenViewRef = null; // clear previous view if any.
  this._updateView();
 }

 @Input()
 set ngIfElse(templateRef: TemplateRef<NgIfContext>) {
  this._elseTemplateRef = templateRef;
  this._elseViewRef = null; // clear previous view if any.
  this._updateView();
 }

 private _updateView() {
  if (this._context.$implicit) {
   if (!this._thenViewRef) {
    this._viewContainer.clear();
    this._elseViewRef = null;
    if (this._thenTemplateRef) {
     this._thenViewRef =
       this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
    }
   }
  } else {
   if (!this._elseViewRef) {
    this._viewContainer.clear();
    this._thenViewRef = null;
    if (this._elseTemplateRef) {
     this._elseViewRef =
       this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context);
    }
   }
  }
 }
}

export class NgIfContext {
 public $implicit: any = null;
 public ngIf: any = null;
}

ngIf的源码并不难,它的核心就在于_updateView函数,它主要通过ViewContainerRef的createEmbeddedView和clear方法来实现模板TemplateRef的呈现和清除(先不关注当中的then和else等的具体实现)。它使用TemplateRef取得<ng-template>的内容,并通过ViewContainerRef来访问这个视图容器。

TemplateRef

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

abstract get elementRef(): ElementRef;
 abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
}

在指令中通过依赖注入TemplateRef可以直接拿到ng-tempalte的TemplateRef,但是在component组件中我们则需要使用viewChild

<ng-template #tptest>
 <span>template test</span>
</ng-template>

@ViewChild('tptest') tptest: TemplateRef<any>;

ViewContainerRef

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

export abstract class ViewContainerRef {
  /*基于TemplateRef对象创建Embedded View(内嵌视图),然后根据`index`指定的值,插入到容器中。 
  如果没有指定`index`的值,新创建的视图将作为容器中的最后一个视图插入。*/ 
 abstract createEmbeddedView<C>(
   templateRef: TemplateRef<C>, //内嵌视图
   context?: C, index?: number): // 创建上下文
   EmbeddedViewRef<C>;
}

createEmbeddedView:context

创建Template 自身 Context 的属性,以ngFor为例:
查看ngFor Context源码:

export class NgForOfContext<T> {
 constructor(
   public $implicit: T, public ngForOf: NgIterable<T>, public index: number,
   public count: number) {}

 get first(): boolean { return this.index === 0; }

 get last(): boolean { return this.index === this.count - 1; }

 get even(): boolean { return this.index % 2 === 0; }

 get odd(): boolean { return !this.even; }
}
<div *ngFor="let hero of heroes; let i=index; let odd=odd">
 ({{i}}) {{hero.name}}
</div>


解析后:

<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" >
 <div>({{i}}) {{hero.name}}</div>
</ng-template>

从例子中可以看到,通过let-i let-odd可以获取到Template的context,这是angular提供的一种语法。因为在 Angular中是没有作用域继承的,所以在模版中无法隐式实现两个无关数据源。一个简单的实现方案就是:一个显式、一个隐式。由于ng-template tag 是写在某个 Component 的 template属性中的,所以在 ng-template tag 之下的部分当然能访问的也只有 Component 作为 Context 提供的属性,从而保持行为的一致性,而如果需要访问到 Template 的 Context,我们就需要使用额外的引入语法。比如 let-i="index",就是把 Template Context 中的 index属性引入到当前的 Component Context 中并赋予别名 i,这样,我们就能够使用 i 这个标识符来访问到 Template Context 中的属性了,并且仍然保持了行为的一致性和作用域的独立性。

模板输入变量是这样一种变量,你可以在单个实例的模板中引用它的值。 这个例子中有好几个模板输入变量:hero、i和odd。 它们都是用let作为前导关键字。

模板输入变量和模板引用变量是不同的,无论是在语义上还是语法上。

我们使用let关键字(如let hero)在模板中声明一个模板输入变量。 这个变量的范围被限制在所重复模板的单一实例上。
而声明模板引用变量使用的是给变量名加#前缀的方式(#var)。 一个引用变量引用的是它所附着到的元素、组件或指令。它可以在整个模板任意位置**访问。

模板输入变量和引用变量具有各自独立的命名空间。let hero中的hero和#hero中的hero并不是同一个变量。

总结:

<ng-template>在ng中主要通过viewChild TemplateRef ViewContainerRef来实现结构性操作。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
浅谈tudou土豆网首页图片延迟加载的效果
Jun 23 Javascript
jquery中post方法用法实例
Oct 21 Javascript
使用Javascript简单实现图片无缝滚动
Dec 05 Javascript
如何使用HTML5地理位置定位功能
Apr 27 Javascript
JS+CSS实现仿触屏手机拨号盘界面及功能模拟完整实例
May 16 Javascript
使用jQuery+EasyUI实现CheckBoxTree的级联选中特效
Dec 06 Javascript
jQuery使用serialize()表单序列化时出现中文乱码问题的解决办法
Jul 27 Javascript
JS仿JQuery选择器功能
Mar 08 Javascript
JS实现json的序列化和反序列化功能示例
Jun 13 Javascript
解决VUE项目localhost端口服务器拒绝连接,只能用127.0.0.1的问题
Aug 14 Javascript
javascript实现左右缓动动画函数
Nov 25 Javascript
手写Spirit防抖函数underscore和节流函数lodash
Mar 22 Javascript
深入理解Vue 单向数据流的原理
Nov 09 #Javascript
node.js基于express使用websocket的方法
Nov 09 #Javascript
angular2系列之路由转场动画的示例代码
Nov 09 #Javascript
使用ef6创建oracle数据库的实体模型遇到的问题及解决方案
Nov 09 #Javascript
基于vue配置axios的方法步骤
Nov 09 #Javascript
微信小程序倒计时功能实现代码
Nov 09 #Javascript
js与jQuery实现的用户注册协议倒计时功能实例【三种方法】
Nov 09 #jQuery
You might like
php 创建以UNIX时间戳命名的文件夹(示例代码)
2014/03/08 PHP
php制作文本式留言板
2015/03/18 PHP
PHP替换Word中变量并导出PDF图片的实现方法
2020/11/26 PHP
Ext.get() 和 Ext.query()组合使用实现最灵活的取元素方式
2011/09/26 Javascript
JavaScript实现拼音排序的方法
2012/11/20 Javascript
自定义ExtJS控件之下拉树和下拉表格附源码
2013/10/15 Javascript
js控制浏览器全屏示例代码
2014/02/20 Javascript
特殊情况下如何获取span里面的值
2014/05/20 Javascript
jQuery动画出现连续触发、滞后反复执行的解决方法
2015/01/28 Javascript
JavaScript创建一个object对象并操作对象属性的用法
2015/03/23 Javascript
javascript字符串对象常用api函数小结(连接,替换,分割,转换等)
2016/09/20 Javascript
Bootstrap CSS组件之面包屑导航(breadcrumb)
2016/12/17 Javascript
Vue中的数据监听和数据交互案例解析
2017/07/12 Javascript
jquery鼠标悬停导航下划线滑出效果
2017/09/29 jQuery
基于node.js实现爬虫的讲解
2019/02/18 Javascript
js实现通过开始结束控制的计时器
2019/02/25 Javascript
layer.confirm()右边按钮实现href的例子
2019/09/27 Javascript
JavaScript 空间坐标的使用
2020/08/19 Javascript
vue axios请求成功却进入catch的原因分析
2020/09/08 Javascript
python插入排序算法的实现代码
2013/11/21 Python
详解C++编程中一元运算符的重载
2016/01/19 Python
Python中的descriptor描述器简明使用指南
2016/06/02 Python
全面了解python中的类,对象,方法,属性
2016/09/11 Python
Python数据拟合与广义线性回归算法学习
2017/12/22 Python
用python一行代码得到数组中某个元素的个数方法
2019/01/28 Python
详解python校验SQL脚本命名规则
2019/03/22 Python
Python 使用 environs 库定义环境变量的方法
2020/02/25 Python
python实现将range()函数生成的数字存储在一个列表中
2020/04/02 Python
俄罗斯建筑和装饰材料在线商店:Stroilandia
2020/07/25 全球购物
Big Green Smile法国:领先的英国有机和天然产品在线商店
2021/01/02 全球购物
某公司部分笔试题
2013/11/05 面试题
高中毕业生自我鉴定
2013/11/03 职场文书
2015年幼儿园班主任个人工作总结
2015/10/22 职场文书
HR必备:销售经理聘用合同范本
2019/08/21 职场文书
浅谈MySQL next-key lock 加锁范围
2021/06/07 MySQL
Mybatis-plus配置分页插件返回统一结果集
2022/06/21 Java/Android