文档章节

Angular 中 ngTemplateOutlet 的用法以及ng-zorro源码分析!

一个人的中台
 一个人的中台
发布于 03/19 11:59
字数 1966
阅读 727
收藏 0

一、引言

今天看到文章:https://segmentfault.com/a/1190000015944548 。于是专门研究一下ngTemplateOutlet用法!!!!

官方定义 :

NgTemplateOutlet: 它是结构指令,根据一个提前备好的 TemplateRef 插入一个内嵌视图。 你可以通过设置 [ngTemplateOutletContext] 来给 EmbeddedViewRef 附加一个上下文对象。 [ngTemplateOutletContext] 是一个对象,该对象的 key 可在模板中使用 let 语句进行绑定。

示例:  <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>       

*我测试:<template>  <ng-template> <ng-container> 都可承载这个指令, 但不支持<div>这样的标签,提示:

使用目的

需要要自定义标题或页脚的内容。 
比如编写弹窗组件,不建议在组件模板中写死标题和页脚的内容,当在页面上使用该组件时, 页面可以动态向指定组件内占位传入“一些内容”,组件会把它们插入到它想要的地方!
    比如ng-zorro中,也大量这样用法,https://ng.ant.design/components/card/zh#components-card-demo-simple

二、组件自定义输入内容

比如在一个页面上,引用nz-card时,把主页面上<ng-template>内容插入到nz-card中去,文档上示例代码:

如果要编写这样的组件,需要考虑几个问题,

1、如何引用主页面上的一个模板元素   ( 使用本地变量 # ,我记得以前官方文档叫“局部模版变量"或“模板引用变量”)
2、引用的变量如何传递给子组件中      (
子组件定义@Input  ,使用类型为TemplateRef<T>的变量接收
3、子组件如何使用这个引用变量          ( 在模版中,用
ngTemplateOutlet  绑定这个变量即可)
4、引用的模板元素从主页面上来,  如何把子组件的数据(即子组件中上下文)传递给这个引用元素上来?(向
ngTemplateOutlet 传入 context: myContext”)

上下文传递很重要。组件为了使用上的灵活,一部分内容定义在组件之外的(即主页面上),当它插入到子组件中的时候,必然要显示子组件内的一些数据,它才有意义。
ngTemplateOutlet 不仅用于绑定元素,还负责把子组件中的一个数据上下文传递进去.

5、模板元素如何使用上下文? (使用 模板输入变量let  的形式,接收上下文属性值,再用  {{  }}语法插入值)

   定义上下文数据时,  myContext = { $implicit: 'World', valueInContent: "子组件内的value" };   
 $implicit:是默认导出值。当let-item  后没= 号时,item引用它
  当 let- valueInContent =valueInContent 时,在模板元素内部可以插值{{valueInContent}}
参考官方文档:

 

完整而精巧的小例子,包含上面5个要点,要认真融会贯通呀

源码如下:

/// 主页面
@Component({
  selector: 'app-root',
  template: `
  <h1>Angular's ngTemplateOutlet 完整示意-----我是主页</h1>
  <app-content [dynamicRef]="usedByContent"></app-content>

  <ng-template #usedByContent let-name let-valueInContent=valueInContent>
  <div style="margin-left:20px;border:dashed;">
    <div>Hello {{name}}!    组件内的上下文绑定: {{valueInContent}} ....</div>
    <div> 主页面的变量绑定: {{valueInApp}}</div>
  </div>
  </ng-template>`,
})
export class AppComponent {
  public valueInApp = "valueInApp :)";
}

/// 一个子组件
@Component({
  selector: 'app-content',
  template: `
     <div>我是子组件,下面的内容是动态加载   :)</div>
    <template
        *ngTemplateOutlet="dynamicRef context: myContext">
    </template>
  `,
})
export class AppContent {
  display = false;
  @Input() dynamicRef: TemplateRef<HTMLDivElement>;
  myContext = { $implicit: 'World',  valueInContent: "子组件内的value" };
}

三、ng-zorro 的组件的实现

支持TemplateRef参数的组件的实现

ng-zorro中,大量的组件的参数支持传入模板引用,它的使用无处不在,要研究框架是如何实现的传入模板引用的参数。 

nz-card为例,它的API说明

凡是支持插入外部的模板引用的参数,   它们的类型就是: string|TemplateRef<void>    或者  TemplateRef<void>

翻看ng-zorro的nz-card 源码,摘引如下,现在看它是不是超级简单:

// 组件TS  
@Input() nzExtra: string | TemplateRef<void>;

// 组件HTML
<div class="ant-card-extra" *ngIf="nzExtra">
      <ng-container *nzStringTemplateOutlet="nzExtra">{{ nzExtra }}</ng-container>
</div>
// 组件TS
 @Input() nzActions: Array<TemplateRef<void>> = [];

// 组件HTML
<ul class="ant-card-actions" *ngIf="nzActions.length">
  <li *ngFor="let action of nzActions">
    <span><ng-template [ngTemplateOutlet]="action"></ng-template></span>
  </li>
</ul>

对于TemplateRef<void>的参数, 直接用[ngTemplateOutlet] 来绑定即可。框架使用ngTemplateOutlet的方式和我们上一节看到的一样。
             我测试 
[ngTemplateOutlet] 不可绑定上下文,但*ngTemplateOutlet就可以,所以我们尽量使用*的格式吧!

 对于string|TemplateRef<void>的参数,用*nzStringTemplateOutlet 来绑定,这是什么鬼东西呢? 

*nzStringTemplateOutlet探险

 *nzStringTemplateOutlet 是ng-zorro官方扩展的指令,很实用且复杂的指令,它即接受string|TemplateRef<void>的两种参数值,插入到子组件的模板中去。
现在要小心翼翼的进入源码探险之旅了!

 

2个知识必备:

1、构造函数注入变量
viewContainer代表指令的宿主元素,此处它承担渲染容器的作用,
defalultTemplate注入的是宿主元素的内容元素,就是上面的{{nzExtra}}部分。

//  注入viewContainer 宿主元素,   注入  defaultTemplate的 TemplateRef<void>
constructor(private viewContainer: ViewContainerRef, private defaultTemplate: TemplateRef<void>) {}

为什么能这样注入变量,这得参见官方文档:

       viewContainer注入:    

      defalultTemplate注入: 

2、动态创建EmbeddedView,  这是动态组件时,经常要使用的函数!

 this.viewContainer.createEmbeddedView( 模版引用,上下文数据  );  //返回一个EmbeddedView

分析源码逻辑

使用nzStringTemplateOutlet的情景如下,nzExtra的值:string  |  TemplateRef<void> 两种可能 :

 <ng-container *nzStringTemplateOutlet="nzExtra">{{ nzExtra }}</ng-container>

1、首先获得各自的  TemplateRef<any> 对象。 (见上图红绿线)
参数为:字符串string时,            通过注入,        {{nzExtra}}  ==> defalultTemplate.
参数为:模板TemplateRef时,     通过指令赋值, 把 变量nzExtra==>inputTemplate.

2、创建EmeddedView。 
使用viewContainer.createEmbeddedView方法,  把TemplateRef和上下文来生成View.

3、监听指令ngOnChanges。
             4、判断是否重新创建视图。因为用户动态修改引用或上下文时,整个视图要重建!
                          是否重建View,就是判断nzStringTemplateOutletContext, nzStringTemplateOutlet 是否变化
                           a)  如果nzStringTemplateOutlet第一次赋值,或nzStringTemplateOutlet变化前后不都是string, 要重建视图
                           b)  如果nzStringTemplateOutletContext 的属性shape有变化时,也要重建视图。
                                                                    (注:新上下文的属性比旧上下文属性个数多时才变化, 若只是属性值变化,是不用更新视图)

              5、若需要重建视图,则重建视图。
                                     6、无需重建视图,则进一步判断是否是TemplateRef参数情况,如果上下文数据变化了,动态更新EmeddedView的值!

通过以上源码,nzStringTemplateOutlet指令就可以监听绑定模板变化,以及上下文数据变化,更新视图。 正由于这个指令强大,方便,我们在使用ng-zorro的组件时,才倍感方便顺手。

绑定上下文件的语法

最后,在ng-zorro源码中,搜索 nzStringTemplateOutlet绑定上下文时的语法,引自nz-form-control源码:

  // ng-zorro的官方写法  
<ng-container *nzStringTemplateOutlet="nzSuccessTip;context:{$implicit:validateControl};">
     {{ nzSuccessTip }}
  </ng-container>

 突然很意外,为什么不是下面这样使用? 为什么写context就能自动赋值到nzStringTemplateOutletContext上去?? 
可能这是由于Angular编译时,特殊的处理吧!

 // 这个写法编译报错,container 不存在nzStringTemplateOutletContext属性
  <ng-container *nzStringTemplateOutlet="nzSuccessTip"  
                [nzStringTemplateOutletContext]="{$implicit:validateControl}“ >
      {{ nzSuccessTip }}
  </ng-container>

四、它的同类指令:NgComponentOutlet

偶然看到Common 模块中,还有NgComponentOutlet指令,根据名称也能猜出它的作用,直接把一个组件绑定过来,此时只要传递一个组件的类名即可!

我想起去年编写实时数据看板的功能时,每一个看板的子组件是根据配置,动态生成一个类的工厂函数,再创建类view,再插入到相应的containerview中去。
当时以为很完美的方法,其实是绕了很多弯路,直接用NgComponentOutlet一句话不就够了吗,当时真的蠢,不过看了许多文章,也没有讲到这个指令的地方。Angular的知识点太多太碎了呀!

 

 

 

 

 

© 著作权归作者所有

一个人的中台
粉丝 18
博文 79
码字总数 49913
作品 0
深圳
程序员
私信 提问
加载中

评论(0)

Angular 实战教程 - Today 系列文章目录

Angular 实战教程 - Today 系列文章目录 发布于 10:15 文章被以下专栏收录

小温
2018/07/18
0
0
阿里云 Angular 2 UI框架 NG-ZORRO介绍

说明: Angular2出来后,一直想找个基于Angular2的前端后台管理框架,但一直没有找到比较适合的。前段时间在Angular官网资源无意之间看到NG-ZORRO,NG-ZORRO由阿里计算平台事业部、阿里云等不...

DonaldTDZ
2017/11/25
0
0
Ant Design 的 Angular 实现 - NG-ZORRO

NG-ZORRO 是一个企业级的 UI 组件库,是 Ant Design 的 Angular 4.0 实现,开发和服务于企业级后台产品。 特性 提炼自企业级中后台产品的交互语言和视觉风格,定期与 Ant Design React 版本保...

VTHINKXIE
2017/08/16
2.3W
25
Angular Material 的设计之美

ng-matero 0.3 已发布,新增 module schematic 以及 page schematic,详见 README 前言 Angular Material 作为 Angular 的官方组件库,无论是设计交互还是易用性都有着极高的质量。正如官方所...

叙帝利
2019/09/05
0
0
NG-ZORRO-MOBILE (Ant Design Mobile of Angular) 移动端UI组件库正式发布

NG-ZORRO-MOBILE 是 Ant Design 移动规范的 Angular 实现。 Ant Design 作为服务于企业级产品的设计体系,基于『确定』和『自然』的设计价值观,通过模块化的解决方案,降低冗余的生产成本,...

NG_ZORRO_MOBILE
2018/10/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Apache OpenMeetings开源线上会议系统——安装配置

OpenMeetings是一个开源的在线会议系统,支持音频和视频,同时支持桌面分享。 官网地址:http://openmeetings.apache.org/index.html 安装 当前版本为4.0.6,需要最低jre8的安装环境。 下载a...

JustForFly
9分钟前
6
0
Android知识体系总结2020之Android部分Handler篇

1.什么是Handler?   Handler是可以通过发送和处理Message和Runnable对象来关联相应线程的MessageQueue。通常我们认为它是一种异步机制。   a.可以让对应的Message和Runnable在未来的某个...

ClAndEllen
11分钟前
10
0
命令模式

命令模式(Command Pattern)在我们生活中非常常见,比如开关灯、电视遥控器换台等。 我们使用摇控器换台的时候,我们按下一个换台的按钮,然后电视机就会换台,按下调整音量调节按钮,电视机...

鸣沙山
13分钟前
11
0
基于 rsync 和 ln 实现“写时复制”的快照备份功能

一、基本原理 这里“写时复制”加了一个引号,因为这是专门针对使用rsync备份时的写时复制效果,而不是事实上的写时复制(copy-on-write),其达到的目的如下: 使用 rsync 备份数据后,立即...

Inpool
49分钟前
25
0
郑州哪哪里可以开工程款发票-郑州_新闻网

【电薇同步;1.3.8 - 2.7.4.1 - 5.2.9.7.】张生、诚、信、合、作,保、真、售、后、保、障、长、期、有、效。adb的全称为Android Debug Bridge,是Android手机通用...

yyqqvip
今天
30
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部