角度-是否可以在子组件中使用内容投影?

时间:2019-10-10 19:18:58

标签: html angular typescript

注意-我意识到可以不使用内容投影而更轻松地完成此示例。我将其用作一个非常简化的示例。

可以说我有以下组件,其中列出了两个不同元素中的名称:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-name-list',
  templateUrl: `
  <div class='large'>
    <h1>Large Names</h1>
    <ng-content select="large"></ng-content>
  </div>
  <div class='small'>
    <h1>Small Names</h1>
    <ng-content select="small"></ng-content>
  </div>
`,
  styleUrls: ['./name-list.component.css']
})
export class TestNgContentComponent {
  constructor() { }
}

然后我可以从具有两个名称列表的模板中调用此组件,如下所示:

<app-names-list>
  <h1 ngProjectAs="large" *ngFor="let n of names">{{ n }}</h1>
  <h2 ngProjectAs="small" *ngFor="let n of names">{{ n }}</h2>
</app-names-list>

请注意,相同的数据用于两个名称列表。是否可以用包含两者的组件替换传递的h1和h2标签,并将它们投影到父对象?例如。像这样的东西:

@Component({
  selector: 'app-name',
  template: `
    <h1 ngProjectAs="large">{{name}}</h1>
    <h2 ngProjectAs="small">{{name}}</h2>
  `,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent  {
  @Input() name: string;
}

,然后将模板修改为:

<app-names-list>
  <app-name *ngFor="let n of names" [name]="n"></app-name>
</app-names-list>

一旦将ngProjectAs指令嵌入app-name模板中,该指令将不再起作用。是否可以从子组件中进行这样的投影?

1 个答案:

答案 0 :(得分:2)

这是我的方法:

我将header tags包裹在ng-template中,以便可以将其附加作为嵌入式视图附加到ng-container上。
您可以了解有关embeddedhost视图here的更多信息。

@Component({
  selector: 'app-name',
  template: `
    <ng-template #large>
      <h1>{{name}}</h1>
    </ng-template>

    <ng-template #small>
      <h2>{{name}}</h2>
    </ng-template>
  `,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent  {
  @Input() name: string;
  @ViewChild('large', { static: true, read: TemplateRef }) large: TemplateRef<any>;
  @ViewChild('small', { static: true, read: TemplateRef }) small: TemplateRef<any>;
}

在这里,我将ng-content替换为ng-container,以便可以附加嵌入的视图。

@Component({
  selector: 'app-name-list',
  template: `
    <div class='large'>
      <h1>Large Names</h1>
      <ng-container #large></ng-container>
    </div>

    <ng-container #foo></ng-container>

    <div class='small'>
      <h1>Small Names</h1>
      <ng-container #small></ng-container>
    </div>
  `,
})
export class TestNgContentComponent {
  @ContentChildren(HelloComponent, { read: HelloComponent }) children: QueryList<HelloComponent>;
  @ViewChild('large', { static: true, read: ViewContainerRef }) largeNamesContainer: ViewContainerRef;
  @ViewChild('small', { static: true, read: ViewContainerRef }) smallNamesContainer: ViewContainerRef;

  constructor() { }

  ngAfterContentInit () {
    this.populateView();
  }

  private populateView () {
    this.largeNamesContainer.clear();
    this.smallNamesContainer.clear();

    this.children.forEach(child => {
      this.largeNamesContainer.createEmbeddedView(child.large);
      this.smallNamesContainer.createEmbeddedView(child.small);
    });
  }
}

Here is a StackBlitz demo