I'm writing a reusable Angular 2 component to retrieve and display data in a table, with server-side sorting, filtering and searching. I would like each row in the table to have an extra column with generic html using an ng-context tag, and I can do this.
The most common case for this is to have buttons which can do actions using the data each row was constructed from (say, navigate to a specific page for the entry, or delete the entry), but there doesn't seem to be a way to pass an object to the ContentChildren of a component. I could perhaps accomplish this via direct DOM manipulation and inspection, but that is both very hacky and bad for re-usability.
I'm not really concerned with any specific way to accomplish this, so long as it:
I'm using typescript so a type-safe solution would be preferred, but I'd be happy having to ignore the type system if need be.
My table component (my-table) in use would look something like
<my-table /* some properties */>
<button class="row-button" type="button" (click)="navigate(/* context */)">Navigate</button>
</my-table>
with its template using ng-content like so:
// table html
<tr *ngFor="let row of rows">
<td *ngFor="let column of row.columns">{{column}}</td>
<td>
<ng-content select=".row-button"></ng-content>
</td>
</tr>
// more table html
If I could pass a parameter to the ng-content which would be usable by the button element that would suffice.
The ng-content is used when we want to insert the content dynamically inside the component that helps to increase component reusability. Using ng-content we can pass content inside the component selector and when angular parses that content that appears at the place of ng-content.
Content projection is a pattern in which you insert, or project, the content you want to use inside another component. For example, you could have a Card component that accepts content provided by another component. With this type of content projection, a component accepts content from a single source.
The first step to passing data into an Angular component is to create a custom property to bind to. This is done via “input” binding to pass data from one component to another (typically parent to child). This custom input binding is created via the @Input() decorator!
One way would be a template variable that allows you to explicitly refer to the parent:
<my-table #myTable>
<button [context]="myTable.context">
</my-table>
As far as I know there are plans to make this more flexible. I guess it is this issue https://github.com/angular/angular/issues/8563
If you're not strict on usin ng-content, ng-template
could do the work:
<my-table [rowButtonTemplate]="buttonTemplate" /* some properties */>
<ng-template #buttonTemplate let-column>
<button class="row-button" type="button" (click)="navigate(column.context)">Navigate</button>
</ng-template>
</my-table>
and then inside table:
// table ts
@Input()
rowButtonTemplate: TemplateRef;
// table html
<tr *ngFor="let row of rows">
<td *ngFor="let column of row.columns">{{column}}</td>
<td>
<ng-container *ngTemplateOutlet="rowButtonTemplate; context: { $implicit: column, index: index }"></ng-container>
</td>
</tr>
// more table html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With