使用 ng-content 嵌入创建动态转发器

Creating a dynamic repeater with ng-content transclusion

我想要实现的是一个绑定到任意对象数组的通用组件,当每行的视图也由使用的主组件用组件任意定义时,允许动态添加和删除行它。

请注意,MasterComponent 是一个任意组件,可以为不同的页面实现,旨在自包含,不由任何元数据或外部源定义。

到目前为止我有以下组件:

RepeaterComponent 模板:

<input type="button" value="Add" (click)="addRow()">
<div class="repeater" *ngFor="let row of repeaterArray">
    <div class="repeaterRow">
        <input type="button" value="Remove" (click)="removeRow(row.rowId)">
        <ng-content select="row"></ng-content>
     </div>
</div>

主组件模板:

<repeater [repeaterArray]="repeaterObj">
    <row>
        <field-textbox [data]="row.name" [label]="'Name'"></field-textbox>
        <field-textbox [data]="row.description" [label]="'Description'"></field-textbox>
    </row>
</repeater>

<field-textbox> 组件是一个自定义组件,我用它来封装简单的输入,其中包含我需要使用的一些额外数据。

MasterComponent 包含一个对象,在这个实例中看起来像这样:

repeaterObj = [
{
    "rowId": 1,
    "name": "First brand",
    "description": "First description"
},
{
    "rowId": 2,
    "name": "Second brand",
    "description": "Second description"
},
{
    "rowId": 3,
    "name": "Third brand",
    "description": "Third description"
}
];

这种方法有两个问题我似乎找不到解决方案。

  1. ngFor 复制模板时,ng-content 选择器对所有行都是相同的,渲染后我只剩下一个 ng-content 包含点。
  2. <field-textbox> 嵌入指令中的 ngFor 声明中没有对 row 变量的引用,因此我无法正确绑定数据。

有没有更好的方法来实现 RepeaterComponent,让我用最少的精力创建更多新的 MasterComponents 不同的任意结构和不同的模板?

您可以使用 ngTemplateOutlet 来实现它。

以下是实现动态转发器的步骤:

第一步是提供一个TemplateRef作为RepeaterComponent:

的子元素
<repeater [repeaterArray]="repeaterObj">
  <ng-template>
    ...
  </ng-template>
</repeater>

第二步 是通过@ContentChild:

RepeaterComponent 内查询此模板
export class RepeaterComponent { 
  @ContentChild(TemplateRef) itemTemplate: TemplateRef<any>;
  ...

第三步是使用ngTemplateOutlet渲染模板:

@Component({
  selector: 'repeater',
  template: `
    <input type="button" value="Add" (click)="addRow()">
    <div class="repeater" *ngFor="let row of repeaterArray">
        <div class="repeaterRow">
            <input type="button" value="Remove" (click)="removeRow(row.rowId)">
            <ng-template <== this line
                    [ngTemplateOutlet]="itemTemplate"
                    [ngTemplateOutletContext]="{ $implicit: row }">
                </ng-template>
        </div>
    </div>`
})
export class RepeaterComponent { 
  @Input() repeaterArray: Array<any>;
  @ContentChild(TemplateRef) itemTemplate: TemplateRef<any>;
  ...
}

第四步是在MasterComponent中使用对TemplateRef里面的row的引用(回到我们的第一步):

<repeater [repeaterArray]="repeaterObj">
  <template let-row>
    <field-textbox [data]="row.name" [label]="'Name'"></field-textbox>
    <field-textbox [data]="row.description" [label]="'Description'"></field-textbox>
  </template>
</repeater>

注意:我们正在传递 ngOutletContext 类似 $implicit 属性 的对象。

using the key $implicit in the context object will set it's value as default.

它的工作原理如下:

[ngTemplateOutletContext]="{ $implicit: row }"  ==> <template let-row>

[ngTemplateOutletContext]="{ item: row }"       ==> <template let-row="item">

ngOutletContext 仅在 Angular 2.0.0-rc.2

2 版本后可用

你可以试试对应的plunkr(更新到5.0.0