了解可重用组件中的ngTemplateOutlet,@ ContentChild,ng-container

时间:2019-02-23 14:50:33

标签: javascript angular

我一直在花更多的时间试图理解以下博客文章Creating Reusable Components with NgTemplateOutlet in Angular

以上帖子的工作代码可以在stackblitz上找到。

UsageExample组件中,调用 card-or-list-view 组件。我非常了解提供了 items mode 输入参数。

现在我不明白的是

<ng-container *cardItem="let item">
        <h1>{{item.header}}</h1>
        <p>{{item.content}}</p>
      </ng-container>
      <span *listItem="let item">
        <h1>{{item.header}}</h1>
        <p>{{item.content}}</p>
      </span>
UsageExample模板中的

替换

<ng-container *ngSwitchCase="'card'">
    <div *ngFor="let item of items" style="margin: 5px;border: black 1px solid">
      <ng-container *ngTemplateOutlet="cardItemTemplate; context: {$implicit: item}">
      </ng-container>
    </div>
  </ng-container>
  <ul *ngSwitchCase="'list'">
    <li *ngFor="let item of items">
      <ng-container *ngTemplateOutlet="listItemTemplate; context: {$implicit: item}"></ng-container>
    </li>
  </ul>

CardOrListViewComponent组件的模板中。在CardOrListViewComponent组件中,声明了两个指令

@ContentChild(CardItemDirective, {read: TemplateRef}) cardItemTemplate;
  @ContentChild(ListItemDirective, {read: TemplateRef}) listItemTemplate;

,并且在其模板中与*ngTemplateOutlet一起使用。

这些指令如何替换为

<ng-container *cardItem="let item">
        <h1>{{item.header}}</h1>
        <p>{{item.content}}</p>
      </ng-container>
      <span *listItem="let item">
        <h1>{{item.header}}</h1>
        <p>{{item.content}}</p>
      </span>

UsageExample组件中。

*cardItem*listItem如何连接到@ContentChild组件中的两个CardOrListViewComponent指令。他们的名字甚至都不相似。

如果有人可以给我详细说明它的工作原理,我将不胜感激。

谢谢。

1 个答案:

答案 0 :(得分:2)

首先,Angular结构指令可以使用两种形式:

1)加糖版本

<div *ngIf="foo">bar</div>

2)减糖版本

<ng-template [ngIf]="foo">
  <div>bar</div>
</ng-template>

回到您的示例:

<ng-container *cardItem="let item">
  <h1>{{item.header}}</h1>                            sugar
  <p>{{item.content}}</p>
</ng-container>

       ||
       \/

<ng-template cardItem let-item>
  <ng-container>
    <h1>{{item.header}}</h1>                         de-sugar
    <p>{{item.content}}</p>
  </ng-container>
</ng-template>

第二,通常的做法是通过@ViewChild(-dren)| @ContentChild(-dren)装饰器引用模板中的内容。

我们可以查询与模板元素匹配的指令。 Angular始终与模板的减糖版本匹配。因此,指令:

@Directive({
  selector: '[cardItem]'
})
export class CardItemDirective {}

匹配模板:

  selector: '[cardItem]' 
               ||
               \/
<ng-template cardItem let-item>

如果您不希望遇到所有可能查询的情况,请遵循以下答案

现在我们知道通过使用@ContentChild查询:

@ContentChild(CardItemDirective) cardItemTemplate;

我们获得了CardItemDirective实例,但是我们想要获得TemplateRef以便在*ngTemplateOutlet中使用它。

这里是read选项的得救之地:

@ContentChild(CardItemDirective, {read: TemplateRef}) cardItemTemplate;
                                     \/
         please give us TemplateRef instance from the element 
         that matches CardItemDirective selector

最后,有了TemplateRef,我们可以在ngTemplateOutlet指令的帮助下进行渲染并传递我们想要的任何上下文:

<div *ngFor="let item of items">
  <ng-container *ngTemplateOutlet="cardItemTemplate; context: {$implicit: item}">