【问题标题】:Creating a dynamic repeater with ng-content transclusion使用 ng-content 嵌入创建动态转发器
【发布时间】:2016-07-27 07:57:10
【问题描述】:

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

请注意,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>

MasterComponent 模板

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

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

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

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

【问题讨论】:

    标签: angular angular2-ngcontent


    【解决方案1】:

    您可以使用ngTemplateOutlet 来实现它。

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

    第一步是提供一个TemplateRef作为RepeaterComponent的子元素:

    <repeater [repeaterArray]="repeaterObj">
      <ng-template>
        ...
      </ng-template>
    </repeater>
    

    第二步是通过@ContentChildRepeaterComponent内查询这个模板:

    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>;
      ...
    }
    

    第四步是在TemplateRef中使用rowMasterComponent中的引用(回到我们的第一步):

    <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>
    

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

    在上下文对象中使用键 $implicit 会将其值设置为 默认。

    它的工作原理如下:

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

    ngOutletContext 仅在 Angular 2 版本 2.0.0-rc.2 之后可用

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

    【讨论】:

    • 看起来这正是我想要的,谢谢!唯一似乎没有按预期工作的是使用 [(ngModel)] 对字段文本框组件进行双向绑定。以下Plunker 演示了当您尝试使用相同的绑定更改两个不同的输入字段时出现的问题。在应用程序组件模板中,您可以看到普通的&lt;input&gt; 绑定按预期工作,并且还会更新绑定的对象,但更改 文本框不会将绑定应用回对象。
    • 尝试使用EventEmitter实现类似plnkr.co/edit/XpEDWD75bfnEtibKDKW7?p=preview的双向绑定
    • 是的,这行得通。我希望绑定能够正确传播,而不必在 组件上实现双向绑定,尽管在带有 [ngTemplateOutlet] 的模板外部使用时,绑定可以工作。
    • 这与没有 ngTemplateOutlet plnkr.co/edit/ZP88WQsexq3UQpLTNriA?p=preview 的行为相同。 ngTemplateOutlet 并不神奇。它只是在 TemplateRef 中传递数据
    • 啊,你是对的。我正在考虑绑定到定义的类实例而不是 JSON 属性。再次感谢!
    猜你喜欢
    • 2017-11-01
    • 2018-01-02
    • 2020-03-18
    • 2017-05-24
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    相关资源
    最近更新 更多