详解Angular中的模板输入变量(let-变量)


本文摘自PHP中文网,作者青灯夜游,侵删。

本篇文章带大家了解一下Angular中的模板输入变量(let-变量)。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

我这个人,写文章或者说心得,不喜欢直接抄官网上面的东西,实在是没啥意思。我还是喜欢用我的大白话来写文章。今天这个关于模板输入变量的这个我今天啃官网啃了许久,总算是初步的了解了是啥东西。

那么模板输入变量到底是什么

我想研究这玩意的起因在于之前我使用ng-zorro的时候,用到了它的分页组件Pagination(官网链接)。这其中有一个自定义上一页下一页模板的功能。代码的话如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

@Component({

  selector: 'nz-demo-pagination-item-render',

  template: `

    <nz-pagination [nzPageIndex]="1" [nzTotal]="500" [nzItemRender]="renderItemTemplate"></nz-pagination>

    <ng-template #renderItemTemplate let-type let-page="page">

      <ng-container [ngSwitch]="type">

        <a *ngSwitchCase="'page'">{{ page }}</a>

        <a *ngSwitchCase="'prev'">Previous</a>

        <a *ngSwitchCase="'next'">Next</a>

        <a *ngSwitchCase="'prev_5'"><<</a>

        <a *ngSwitchCase="'next_5'">>></a>

      </ng-container>

    </ng-template>

  `

})

export class NzDemoPaginationItemRenderComponent {}

看完这个之后我就很是疑惑,这个let是啥,为啥let-跟上一个变量之后就有值了呢?然后我就开始在官网中找这个let是什么东西。最终,我在主要概念-指令-结构性指令微语法一节找到了关于let的说明。官网描述:微语法。【相关推荐:《angular教程》】

在下面还有一段简短的说明:

模板输入变量(Template input variable)

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

......

你使用 let 关键字(如 let hero)在模板中声明一个模板输入变量。 这个变量的范围被限制在所重复模板的单一实例上。 事实上,你可以在其它结构型指令中使用同样的变量名。

......

官网还是一如既往的不说人话,短短几句话就给你介绍完了,也不告诉你这玩意怎么用,也不告诉你let声明的变量的值到底是从哪里来的。我越看越气,得,官网你不说,我自己找。

先说一个粗略的结论:let声明的变量是这个template模板的上下文对象中的变量。不然为啥叫模板输入变量呢。在*ngFor内幕这小节中,我们了解到了其内幕,结构性指令其实就是将宿主元素包裹在一个<ng-template></ng-template>中,然后在这个模板上将*ngFor中的表达式解析成一个个的let模板输入变量和这个指令需要传入的值。由于模板中代码并不会直接被渲染成视图,所以一定需要某种方法来使模板变成视图。我们的结构性指令就是干这个事的,就是将我们的模板变成视图。

*ngFor的官方示例代码如下:

1

2

3

4

5

6

7

8

//解析前的模板

<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">

  ({{i}}) {{hero.name}}

</div>

//angular解析后的模板

<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">

  <div [class.odd]="odd">({{i}}) {{hero.name}}</div>

</ng-template>

  • 在此提示一下,所谓的宿主元素就是指令所在的那个元素,像上面的例子中,div就是*ngFor指令的宿主元素。
  • trackBy这个类似于vue和react中的key。可以给模板一个标识,使其重新渲染的时候性能开销降低一些。

然后我们再看官网说的:

  • let-ilet-odd 变量是通过 let i=indexlet odd=odd 来定义的。 Angular 把它们设置为上下文对象中的 indexodd 属性的当前值。
  • 这里并没有指定 let-hero 的上下文属性。它的来源是隐式的。 Angular 将 let-hero 设置为此上下文中 $implicit 属性的值, 它是由 NgFor 用当前迭代中的英雄初始化的。

看完这段描述,我们可以得知:angular为这个模板设置了上下文对象。但是我们看不到这个过程,因为这是在ngFor的源码内部实现的。并且这个上下文对象具备indexodd属性,并且包含一个$implicit(implicit:含蓄的;不直接言明的)的属性。那么我们推断这个上下文对象至少有以下几个属性:

1

2

3

4

5

{

    $implicit:null,

    index:null,

    odd:null,

}

那么我们声明let变量的本质其实就是声明一个变量获取上下文对象中的同名属性的值let-hero不进行任何赋值的话,hero默认等于$implicit的值。无论是有多少个let-alet-blet-c还是let-me。声明的这个变量的值都等于$implicit的值。而我们进行赋值过的,比如let-i = "index"i的值就等于这个上下文对象中的index属性对应的值。

上下文对象是如何设置的

当我们知道这个上下文对象是什么了,就该想这个上下文对象是怎么设置的了

结构性指令这一节当中,我们跟着官方示例做了一遍这个自定义结构性指令(如果还没有做的话,建议先跟着做一遍)。在UnlessDirective这个指令中,其构造器constructor声明了两个可注入的变量,分别是TemplateRefViewContainerRef。官网给的解释我觉得太过晦涩难懂,我这里给出一下我自己的理解:TemplateRef代表的是宿主元素被包裹之后形成的模板的引用。而ViewContainerRef代表的是一个视图容器的引用。那么问题来了,这个视图容器在哪儿呢?我们在constructor构造器中打印一下ViewContainerRef。打印结果如图:

1.png

然后我们点进去这个comment元素。发现其就是一个注释元素。如图所示:

2.png

其实我也不是很确定这个视图容器到底是不是这个注释元素。但是毋庸置疑的是,视图容器和宿主元素是兄弟关系,紧挨着宿主元素。我们可以使用ViewContainerRef中的createEmbeddedView() 方法(Embedded:嵌入式,内嵌式),将templateRef模板引用传入进去,创建出来一个真实的视图。由于这个视图是被插入到视图容器ViewContainerRef中了,所以又叫内嵌视图。那么这又和我们的上下文对象有什么关系呢?其实createEmbeddedView这个方法不止一个参数,其第二个参数就是给我们的模板设置上下文对象的。API的详情介绍请看createEmbeddedView这个API的详情。

阅读剩余部分

相关阅读 >>

深入了解Angular中的component组件

浅谈Angular中的$injector对象

详解Angular中的依赖注入模式

浅谈Angular路由跳转中的navigatebyurl和navigate

浅谈Angular中导入本地json文件的方法

2021年值得尝试的7个Angular前端组件库,快来收藏吧!

深入了解Angular中的hostbinding和hostlistener装饰器

了解Angular中的变化检测(change detection)机制

Angular的$watch方法详解

AngularAngularjs的简单比较

更多相关阅读请进入《Angular》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...