如何获得 ng-content select 过滤器来处理投影模板内容?

How can I get an ng-content select filter to work with projected template content?

我有一个用于呈现列表的 List 组件。 (好吧,我不知道,但我试图将我的问题提炼成一个易于理解的简单示例)。

List 组件的模板有一个或多个 ListItem 允许定义列表项的组件,如下所示:

<app-list>
  <app-list-item text='foo'></app-list-item>
  <app-list-item text='bar'></app-list-item>
</app-list>

...应呈现为:

  • foo
  • bar

我也有(假设)一个使用 List 组件的 Reminder 组件。 Reminder 组件有一个 deadline 属性,在此截止日期前要做的事情列表在组件的模板中定义,我们使用一个或多个 ListItem 组件之前看过:

<app-reminder deadline='Today'>
  <app-list-item text='foo'></app-list-item>
  <app-list-item text='bar'></app-list-item>
</app-reminder>

这应该呈现为:

Remember to do the following by Today:

  • foo
  • bar

List组件很简单:

@Component({
  selector: 'app-list',
  template: `
    <ul>
      <ng-content></ng-content>
    </ul>
  `
})
export class ListComponent{
  @ContentChildren(ListItemComponent) public readonly items: QueryList<ListItemComponent>;
}

ListItem组件更简单:

@Component({
  selector: 'app-list-item',
  template: '<li>{{text}}</li>'
})
export class ListItemComponent {
  @Input() public text;
}

最后,Reminder 组件也非常简单:

@Component({
  selector: 'app-reminder',
  template: `
    <h2>Remeber to do the following by {{deadline}}</h2>
    <app-list>
      <ng-content></ng-content>
    </app-list>
  `
})
export class ReminderComponent {
  @Input() public deadline: string;
}

将这些组件与上面显示的模板片段一起使用效果很好。您可以在 this StackBlitz.

中看到实际效果

现在进入问题的重点。 List组件和Reminder组件都使用<ng-content>。在这两种情况下,我们真的不想将 所有 内容投影到列表中 - 只是 <app-list-item> 元素。

如果我像这样更改 Reminder 组件模板中的 <ng-content> 标记:

<ng-content select='app-list-item'></ng-content>

...那么该组件仍然有效,并且排除了其模板中的任何其他内容,这正是我们想要的。

如果我对 List 组件模板中的 <ng-content> 标记进行相同的更改,这也适用于像这样的简单模板:

<app-list>
  <app-list-item text='foo'></app-list-item>
  <app-list-item text='bar'></app-list-item>
  <h1>EXCLUDE ME</h1>
</app-list>

但是,最后的更改(将 select 过滤器添加到 List 组件模板中的 <ng-content> 元素)停止了 Reminder 工作中的组件。提醒中没有呈现列表项。

我想这可能是因为 Reminder 组件模板渲染的 List 组件看到了 渲染的 内容(<li>标签)而不是 模板 内容(<app-list-item> 标签)。

我在这里似乎有一个不愉快的选择 - 我可以 限制将由 List 组件呈现的内容类型(在这种情况下任何旧的垃圾都可能被包括在内),或者在创建其他组件时失去使用 List 组件的能力。

还是我遗漏了什么?还有其他方法吗?

我设法通过使用 ngProjectAs 解决了这个问题。

reminder.component.ts

@Component({
  selector: 'app-reminder',
  template: `
    <h2>Remeber to do the following by {{deadline}}</h2>
    <app-list>
      <ng-container ngProjectAs="'app-list-item'">
        <ng-content select='app-list-item'></ng-content>
      </ng-container>
    </app-list>
  `
})
export class ReminderComponent {
  @Input() public deadline: string;
}

Here is the StackBlitz demo.