Reputation: 969
For example, suppose I want to implement a datatable component to which I can pass an optional template for the cells of each column:
@Component({
selector: 'datatable',
template: `
<table>
<tbody>
<tr *ngFor="let row of rows">
<td *ngFor="let col of columns">
<ng-container *ngIf="getTemplate(col)">
<ng-container *ngTemplateOutlet="getTemplate(col)"
ngTemplateOutletContext="{$implicit: row}">
</ng-container>
</ng-container>
<ng-container *ngIf="!col.cellTemplate">
{{row[col.prop]}}
</ng-container>
</td>
</tr>
</tbody>
</table>`,
})
export class DatatableComponent implements OnInit {
@ContentChildren('cellTemplate') cellTemplates: QueryList<TemplateRef<any>>;
...
}
Which I want to use as so:
<datatable [columns]='columns'
[rows]='rowCollection'>
<ng-template #cellTemplate let-row prop="id">
<a href='some url'> {{row.id}} </a>
</ng-template>
<ng-template #cellTemplate let-row prop="name">
<a href='some other url'> {{row.name}} </a>
</ng-template>
</datatable>
The problem I have is: how do I implement the function getTemplate, which should find the correct template in cellTemplates for each column? For example, I have given each template an attribute prop
but I don't see how I can access this value from the TemplateRef.
Upvotes: 0
Views: 487
Reputation: 93
I also had a fair bit of struggle figuring this out, Angular does a poor job of it. Needing a custom directive to identify each template is a surprising bit of a hack.
So just to expand on the answer from RichardW, the @ContentChildren can be referenced in the DataTable component with a function that finds the right template - where the propName matches a table column name.
showCellTemplate(column = "") {
return this.cellTemplates.find((obj) => obj.propName === column);
}
In the DataTable component HTML, add a container that is replaced by the template that matched the named column - using *ngTemplateOutlet
. Note the template's inner TemplateRef is exposed as .tmpl
from the directive's constructor and needs to be referenced (otherwise it throws an error.)
<ng-container *ngIf="showCellTemplate(col) as template">
<ng-container *ngTemplateOutlet="template.tmpl;
context: { $implicit: row }"></ng-container>
</ng-container>
The table row data is sent back to the template as the context
and can be accessed with an attribute on the template, e.g. let-row
.
<ng-template #cellTemplate="propName" propName="name" let-row>
<a href='some other url'> {{row.name}} </a>
</ng-template>
Upvotes: 0
Reputation: 969
I figured this out after a fair bit of struggle. The solution is:
Code:
@Directive({selector: '[propName]', 'exportAs': 'propName'})
export class PropName {
constructor (public tmpl: TemplateRef<any>) { }
@Input('propName') propName: string;
}
...
<datatable [columns]='columns'
[rows]='rowCollection'>
<ng-template #cellTemplate="propName" propName="id">
<a href='some url'> {{row.id}} </a>
</ng-template>
<ng-template #cellTemplate="propName" propName="name">
<a href='some other url'> {{row.name}} </a>
</ng-template>
</datatable>
Each element of the ContentChildren list (defined like @ContentChildren('cellTemplate') cellTemplates
) will then have both a propName property (identifying the template) and a tmpl property (the TemplateRef).
Upvotes: 2