【问题标题】:Angular- Is it possible to use content projection from within a child component?Angular-是否可以在子组件中使用内容投影?
【发布时间】:2019-10-11 04:36:24
【问题描述】:

注意——我意识到这个例子可以在不使用内容投影的情况下更容易地完成。我将其用作一个非常简化的示例。

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

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 模板中,这将不再有效。有没有办法在子组件中进行这样的投影?

【问题讨论】:

    标签: html angular typescript


    【解决方案1】:

    这是我的方法:

    我将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.

    【讨论】:

    • 好节目 Andrei Gătej - 当我看到你的回复通过时,我正在写自己的回复。
    • 太棒了!这似乎工作得很好。如果我使用的不是简单的

      ,而是带有输入/输出的更复杂的组件,这是否会分崩离析?基本上我要做的是让一个组件(在示例中为app-name)管理同时出现在页面上(在不同位置)的两个单独的“模板”(h1h2元素)。我扩展了我的示例,其中名称实际上是一个将名称作为输入的新组件,它似乎工作得很好。

    • 很高兴它成功了!我认为你可以在 app-name 中添加几乎所有你想要的东西,只要它被一个 ng-template 包装。 ng-temaplate 有魔力!当您从视图中引用一个组件并将其读取为“TemplateRef”时,该变量将有效地保存该 ng-template 内部的内容(被包装的内容)。在 ng-container 的帮助下,您可以轻松地将这些嵌入式视图附加到任何您想要的位置。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-21
    • 2019-03-23
    • 1970-01-01
    • 2021-10-08
    相关资源
    最近更新 更多