Creating a dynamic follower with ng content enabled

I am trying to achieve a universal component that is associated with an array of arbitrary objects, which allows you to dynamically add and delete rows, when the representation of each row is also arbitrarily determined with the components by the main component that uses it.

Please note that MasterComponent is an arbitrary component that will be implemented for different pages and should be autonomous and not defined by any metadata or an external source.

What I have are the following components:

RepeaterComponent 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-content select="row"></ng-content> </div> </div> 

MasterComponent Template :

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

The <field-textbox> component is a custom component that I use to encapsulate simple input that contains some additional data that I need to use.

MasterComponent contains an object that for this instance looks like this:

 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" } ]; 

This approach has two problems for which I cannot find a solution.

  1. The ng-content selector is the same for all lines when ngFor duplicates the template, and after rendering, I only have one point ngFor ng-content ngFor .
  2. The <field-textbox> directives do not have a reference to the row variable from the ngFor so I cannot bind the data correctly.

Is there a better approach for implementing RepeaterComponent that would give me the least effort to create more new MasterComponents different arbitrary structures and different patterns?

+6
source share
1 answer

You can use ngTemplateOutlet to achieve this.

Following are the steps for implementing a dynamic repeater:

The first step is to provide the TemplateRef as a child of the RepeaterComponent :

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

The second step is to request this template in RepeaterComponent via @ContentChild :

 export class RepeaterComponent { @ContentChild(TemplateRef) itemTemplate: TemplateRef<any>; ... 

The third step is to use ngTemplateOutlet to render the template:

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

The fourth step is to use the row link inside the TemplateRef in the MasterComponent (back to our first step):

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

Note: we are ngOutletContext as an object with the $implicit property.

using the $ implicit key in a context object will set its default value.

This works as follows:

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

ngOutletContext is only available from version 2.0.0-rc.2 for Angular 2

You can try the appropriate plunkr (updated to 5.0.0 )

+11
source

All Articles