如何循环遍历节元素以使用 ng-content 进行投影

How to loop through section elements to project with ng-content

我正在构建步进器并使用 "transclusion" 和 ng-content 来动态抓取步进器标记内的 section 元素。 stepper.component 视图是这样工作的:

<ng-content select=".one"></ng-content>
<ng-content select=".two"></ng-content>
<ng-content select=".three"></ng-content>

组件用法如下所示:

<stepper>
    <section class="one">content here<section>
    <section class="two">content here<section>
    <section class="three">content here<section>
</stepper>

但是,我想通过自动识别节元素使其动态化:

<ng-content *ngFor="let section of sections; index as i;" select="['section:nth-child(' + i + ')']"></ng-content>

我怎样才能:

  1. 获取可用于嵌入的部分元素的节点列表?
  2. 使用 ng-content 的 select 逐步定位他们?

我会创建一个像这样的指令:

@Directive({
  selector: '[stepper-section]'
})
export class StepperSectionDirective {}

然后向每个部分添加 stepper-section 属性:

<stepper>
    <section stepper-section>content here<section>
    <section stepper-section>content here<section>
    <section stepper-section>content here<section>
</stepper>

最后使用 @ContentChildren 装饰器查询所有部分:

@ContentChildren(StepperSectionDirective) sections: QueryList<StepperSectionDirective>;

Ng-run Example

如果你想遍历内容并动态渲染它,你可以用 ng-template 包装你的 children 并使用 ngTemplateOutlet 指令在 StepperComponent 中渲染它们:

html

<app-stepper>
  <ng-template stepper-section>Content 1</ng-template>
  <ng-template stepper-section>Content 2</ng-template>
  <ng-template stepper-section>Content 3</ng-template>
</app-stepper>

stepper-section.directive.ts

@Directive({
  selector: '[stepper-section]'
})
export class StepperSectionDirective {
  hidden = false;

  constructor(public templateRef: TemplateRef<any>) {}
}

stepper.component.ts

@ContentChildren(StepperSectionDirective) sectionDirs: QueryList<StepperSectionDirective>;

stepper.component.html

<button *ngFor="let section of sections; index as i;"
       [class.enabled]="activeSection === i" (click)="goTo(i)">
  Step {{ i + 1 }}
</button>
<div class="content">
  <ng-container *ngFor="let section of sections">
    <ng-template *ngIf="!section.hidden" 
         [ngTemplateOutlet]="section.templateRef"></ng-template>
  </ng-container>
</div>

Ng-run Example

这两种方法的区别在于,在第一种情况下,所有 child 内容都会被渲染,而我们只对 display: none 我们想要隐藏的步骤进行操作。

在第二种方法中,我们可以更好地控制要渲染的内容,并且我们只在特定时间渲染一个步骤。