Angular:使用 ngTemplateOutlet 构建更多动态组件🎭简介定义用例 #1:上下文感知模板用例 #2:模板重载用例 #3:树总结

2025-06-10

Angular:使用 ngTemplateOutlet 构建更多动态组件🎭

介绍

定义

用例 #1:上下文感知模板

用例 #2:模板重载

用例 #3:树

总结

介绍

为了构建可复用且开发者友好的组件,我们需要使其更具动态性(即更具适应性)。好消息是,Angular 提供了一些很棒的工具来实现这一点。例如,我们可以使用以下命令将内容注入到组件中<ng-content>



@Component({
  selector: 'child-component',
  template: `
    <div class="child-component">
      <ng-content></ng-content>
    </div>
  `,
})
export class ChildComponent {}

@Component({
  selector: 'parent-component',
  template: `
    <child-component>
      Transcluded content
    </child-component>
  `,
})
export class ParentComponent {}


Enter fullscreen mode Exit fullscreen mode
代码片段 1:Transclusion

虽然这种嵌入技术对于简单的内容投影来说非常有效,但如果您希望投影的内容具有上下文感知能力,该怎么办呢?例如,在实现列表组件时,您希望在父组件中定义项目模板,并使其具有上下文感知能力(即当前托管的项目是什么)。
对于这类场景,Angular 提供了一个非常棒的 AP​​I,叫做ngTemplateOutlet!

在本文中,我们将定义什么ngTemplateOutlet是 Angular,然后构建上面提到的列表组件以及卡片组件,以了解两个最常见的ngTemplateOutlet用例。我们将逐步实现这些组件,因此在本文结束时,您应该能够轻松地在 Angular 组件中使用这些组件 :)

定义

从当前的 Angular 文档中ngTemplateOutlet可以得知一条指令:从准备好的 TemplateRef 插入嵌入视图

该指令有两个属性:

  • ngTemplateOutlet:模板引用(类型:)TemplateRef
  • $implicitngTemplateOutletContext:要附加到 EmbeddedViewRef 的上下文对象。使用上下文对象中的键将把其值设置为默认值。

这意味着,在子组件中,我们可以从父组件获取一个模板,并将一个上下文对象注入到这个模板中。然后,我们就可以在父组件中使用这个上下文对象了。

如果您觉得这太抽象,这里有一个如何使用它的例子:



<!-- Child component -->
<child-component>
  <ng-container
    [ngTemplateOutlet]="templateRefFromParentComponent"
    [ngTemplateOutletContext]="{ $implicit: 'Joe', age: 42 }"
  >
  </ng-container>
</child-component>

<!-- Parent component -->
<parent-component [templateRefFromParentComponent]="someTemplate">
  <ng-template #someTemplate let-name let-age="age">
    <p>{{ name }} - {{ age }}</p>
  </ng-template>
</parent-component>


Enter fullscreen mode Exit fullscreen mode
代码片段 2:ngTemplateOutlet 用法

在上面的代码中,子组件将包含一个包含“Joe - 42”的段落。
请注意,对于 name ( let-name),我们没有指定要使用上下文对象的哪个属性,因为 name 存储在该$implicit属性中。另一方面,对于 age ( let-age="age"),我们指定了要使用的属性名称(在本例中为age)。

好了,定义就这么多。让我们开始写代码吧。

本文中展示的代码可以在 Github 仓库中找到

用例 #1:上下文感知模板

让我们构建一个从其父级获取两个输入的列表组件:

  1. 数据:对象列表。
  2. itemTemplate:用于表示列表的每个元素的模板。

运行ng new templateOutletTutorial --minimal生成一个小型 Angular 项目来编写代码

让我们使用 Angular 原理图 ( ) 生成列表组件ng g c components/list。完成后,让我们实现该组件,它将显示 data 属性(输入列表)的每个项目。在 的每次迭代中ng-for,它都会在 itemTemplate 属性中插入父组件提供的嵌入视图。执行此操作时,组件应附加一个包含当前项目的上下文对象。
最终,列表组件应如下所示:



@Component({
  selector: 'app-list',
  template: `
    <ul class="list">
      <li class="list-item" *ngFor="let item of data">
        <ng-container
          [ngTemplateOutlet]="itemTemplate"
          [ngTemplateOutletContext]="{ $implicit: item }"
        ></ng-container>
      </li>
    </ul>
  `,
  styleUrls: ['list.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ListComponent {
  @Input() data: any[];
  @Input() itemTemplate: TemplateRef<HTMLElement>; // a template reference of a HTML element
}


Enter fullscreen mode Exit fullscreen mode
代码片段 3.1:列表组件实现

然后在父组件中,我们需要使用列表(对象)和模板引用来调用列表组件:



<app-list
  [itemTemplate]="customItemTemplate"
  [data]="[{ id: 4, name: 'Laptop', rating: 3 },
    { id: 5, name: 'Phone', rating: 4 },
    { id: 6, name: 'Mice', rating: 4 }]"
>
  <ng-template #customItemTemplate let-item>
    <div style="display: flex; justify-content: space-between;">
      <span> {{ item.id }} - <b>{{ item.name }}</b> </span>
      <mark> Stars: {{ item.rating }} </mark>
    </div>
  </ng-template>
</app-list>


Enter fullscreen mode Exit fullscreen mode
代码片段 3.2:父组件模板

请注意,我们将 ng-template(项目模板)放在了 app-list 组件标签内。这只是为了方便阅读,您可以将项目模板放在父模板中的任何位置。
此外,我在项目模板中添加了一些内联样式,但您也可以为其添加一个类名,并在父组件样式文件中设置样式。

用例 #2:模板重载

我们了解了如何ngTemplateOutlet帮助我们投射上下文感知模板,让我们看看另一个很好的用例:模板重载。

为此,我们将构建一个由两部分组成的卡片组件:

  1. 标题:卡片的标题。
  2. 内容:卡片的主要内容。

对于标题,我们将传递一个简单的字符串,对于内容,我们可以使用内容投影来注入它。在使用 Angular 原理图 ( ng g c components/card) 创建卡片组件后,我们就可以这样做了,该组件应如下所示:



@Component({
  selector: 'app-card',
  template: `
    <div class="card">
      <header>{{ title }}</header>
      <article>
        <ng-content></ng-content>
      </article>
    </div>
  `,
  styleUrls: ['card.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CardComponent {
  @Input() title: string;
}


Enter fullscreen mode Exit fullscreen mode
代码片段 4.1:带有字符串“title”的卡片组件

我们在父组件模板中调用它:



<app-card [title]="'hello there'">
  <p>i'm an awesome card.</p>
</app-card>


Enter fullscreen mode Exit fullscreen mode
代码片段 4.2:带有字符串 *title* 的父组件模板

现在假设我们想<img>在标题中放置一张图片(),或者在标题模板中使用其他组件。由于 title 属性只能接受字符串,我们可能会遇到困难。
为了解决这个问题,我们可以在卡片组件中实现一个新的行为。我们可以将标题定义为字符串或 TemplateRef。如果是字符串,我们将使用字符串插值将其绑定到模板;否则,我们将使用ngTemplateOutlet
实施这些更改后,新的卡片组件应如下所示:



@Component({
  selector: 'app-card',
  template: `
    <div class="card">
      <header *ngIf="isTitleAString(); else titleTemplateWrapper">{{ title }}</header>
      <ng-template #titleTemplateWrapper>
        <ng-container [ngTemplateOutlet]="title"></ng-container>
      </ng-template>
      <article>
        <ng-content></ng-content>
      </article>
    </div>
  `,
  styleUrls: ['card.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CardComponent {
  @Input() title: string | TemplateRef<HTMLElement>;
  isTitleAString = () => typeof this.title == 'string';
}


Enter fullscreen mode Exit fullscreen mode
代码片段 4.3:带有 strTemplateRef *title* 的卡片组件

我们在父组件模板中这样调用它:



<app-card [title]="title">
  <ng-template #title> <h2>Hello there</h2> </ng-template>
  <p>i'm an awesome card.</p>
</app-card>


Enter fullscreen mode Exit fullscreen mode
代码片段 4.4:带有 TemplateRef *title* 的父组件模板

用例 #3:树

总结

总结 gif

好了,我们了解了什么ngTemplateOutlet是 TensorFlow,以及如何利用它。我们了解了 3 个最常见的用例,但现在您已经了解了这项技术,也许您还能发现另一个很棒的用例!


这篇文章就到这里。希望你喜欢。如果喜欢,请分享给你的朋友和同事。你也可以在 Twitter 上关注我@theAngularGuy,这会对我有很大帮助。

祝你有美好的一天 !


接下来读什么?

鏂囩珷鏉ユ簮锛�https://dev.to/mustapha/angular-build-more-dynamic-components-with-ngtemplateoutlet-3nee
PREV
为什么 HTML 同时有 <b/i> 和 <strong/em>?
NEXT
关于 Angular 动画你需要知道的一切 1. 创建主组件 📦 2. 实现列表组件(暂时没有动画) 3. 动画 🧙‍♂️ 结论