在 *ngFor 的情况下,是啥为 ngTemplate 提供了上下文?

Posted

技术标签:

【中文标题】在 *ngFor 的情况下,是啥为 ngTemplate 提供了上下文?【英文标题】:What gives the context to ngTemplate in case of *ngFor?在 *ngFor 的情况下,是什么为 ngTemplate 提供了上下文? 【发布时间】:2021-08-22 05:04:34 【问题描述】:

我对 ngTemplateOutletContext 有一些困惑,特别是它是如何传递给 ngTemplate 或谁将它传递给 ngTemplate 的,它围绕着应用了 *ngFor 的元素。当我将它与我们显式传递要渲染的 ngTemplate 进行比较时,混淆就会增加。

这是我认为当我们显式传递模板时会发生的情况 -

假设我们有一个ngContainer / ngTemplate / ( Literally any html element as ngTemplateOutlet & ngTemplateOutletContext can be applied to any element),现在我们选择显式提供一个我们之前创建的模板。像这样-

<ng-template #temp let-number>
  <p>number</p>
</ng-template>

然后我们将它传递给我们已经应用 ngTemplateOutletngTemplateOutletContext 的元素,就像这样 -

<div [ngTemplateOutlet]="temp" [ngTemplateOutletContext]="$implicit: 4">
</div>

(我们应该在上面的操作中使用ng-container,因为它不在 DOM 中,但我在这里不关心。)

现在,temp ngTemplate 将具有与 div(或任何其他父容器)传递的内容一样的上下文 -

我在这里理解的是,我们删除了ngTemplateOutlet 中提供的ng-template,并在ngTemplateOutletContext 中传递了上下文。我们创建局部变量以在模板本身中使用。


问题


当我们使用*ngFor 时-

<p *ngFor="let number of data">
  number
</p>

它通过以下方式去糖-

<ng-template #temp ngFor [ngForOf]="data" let-number>
  <p>number</p>
</ng-template>

ts 文件中的data 变量是data = [1,2,3]

The question is-

1.) 谁提供值 ( context ) 给这个去糖的 ng-template 以便它能够将 let-number 绑定到 $implicit 属性.我查看了 ngForOfContext ,在那里我发现提供了一些变量,这就是为什么我们能够绑定变量,如 evenindex 之类 -

<p *ngFor="let number of data; let i=index; let isEven=even">

但是在这种情况下谁将这些变量提供给ng-template。 (当我们显式传递模板时,父容器(此处为div)提供了我们之前看到的上下文)

2.) 当我们设置ngForTemplate 时,它需要一个TemplateRef 类型的模板,如下所示-

p 与 *ngFor 指令一起使用是允许的,但在使用 ngFor 的脱糖版本时显式设置 p 的引用是不允许的,即 -

<ng-template #temp ngFor [ngForOf]="data" [ngForTemplate]="pTemplate" let-number>

pTemplate 在哪里 -

<p #pTemplate *ngFor="let number of data; let i=index; let isEven=even">
  number i isEven
</p>

但它无法识别 ptemplate 中的 ngForTemplate 。如果它不能将pTemplate 识别为有效的ngForTemplate,它如何能够在*ngFor 版本中剔除p

PS:如果我在提问前对ng-template的理解有误,我也很乐意纠正。

【问题讨论】:

【参考方案1】:

当我们使用 ngFor 指令时:

<ng-container *ngFor="let number of data;let i = index;let isEven=even">
    <p [class.even]="isEven">i : number</p>
</ng-container>

内部 html 是一个 ng-template。 ngFor 指令为循环的每次迭代提供此模板的上下文。

正如您所指出的,在 ngFor 指令中有一个 ngForTemplate 输入。我们可以使用它来设置在 ngFor 内部 Html 之外声明的模板

<ng-template #pTemplate let-number let-isEven="even" let-i="index">
    <p [class.even]="isEven">i : number</p>
</ng-template>


<ng-container *ngFor="let number of data;let i = index;let isEven=even;template: pTemplate">
</ng-container>

所以 ngFor 指令为这个模板提供了上下文。此上下文的类型为 ngForOfContext

我们可以通过自己提供上下文来将此模板与 ngTemplateOutlet 一起使用:

<ng-container *ngTemplateOutlet="pTemplate;context:$implicit:43,even:true,index:2">
</ng-container>

但 *ngFor 语法是 ngTemplate 的语法糖。我们可以像这样在不加糖的情况下使用它:

<ng-template ngFor [ngForOf]="data"  let-number let-isEven="even" let-i="index">
    <p [class.even]="isEven">i : number</p>
</ng-template>

这里的 ng-template 仍然是每次迭代应用的模板。我们还可以通过提供上下文在 ngTemplateOutlet 中使用它:

<ng-template #templ ngFor [ngForOf]="data"  let-number let-isEven="even" let-i="index">
    <p [class.even]="isEven">i : number</p>
</ng-template>

<ng-container *ngTemplateOutlet="templ;context:$implicit:43,even:true,index:2">
</ng-container>

但是如果使用带有ngForTemplate 输入的去糖语法,我们必须声明第二个 ng-template 元素。因为如果我们尝试将不带 * 的 ngFor 指令应用于 ng-template 以外的任何其他内容,我们会收到错误。

如果我们查看source code of the ngFor directive,我们可以看到它的构造函数中需要一个templateRef。但是这个模板 ref 的值也是由 ngForTemplate 输入设置的。

所以我们需要写

<ng-template #pTemplate let-number let-isEven="even" let-i="index">
    <p [class.even]="isEven">i : number</p>
</ng-template>

<ng-template ngFor [ngForOf]="data" [ngForTemplate]="pTemplate"></ng-template>

<!-- And we cannot write : -->
<ng-container ngFor [ngForOf]="data" [ngForTemplate]="pTemplate"></ng-container>

在这种情况下,我们有 2 个模板,但只使用了一个,因此我们放置 ngFor 的模板不需要上下文,它的唯一目的是能够调用 ngFor 指令。

希望这能回答您的问题。

【讨论】:

以上是关于在 *ngFor 的情况下,是啥为 ngTemplate 提供了上下文?的主要内容,如果未能解决你的问题,请参考以下文章

表单的开始和结束以啥为标志?表单的提交有几种方法,它们的区别是啥?

“*ngFor”中的“*”是啥意思? [复制]

如何在没有 ngFor 和没有另一个 @Component 的情况下多次重复一段 HTML?

将 *ngFor 与迭代器一起使用的正确方法是啥?

如何在不创建数组的情况下使用 NgFor 来生成矩阵 UI 模式

Linux下怎么建立任务计划,shell文件是做啥的?以啥为后缀,前两者之间有啥关系