I am building a standalone component that receives a collection of objects to render with ngFor. The templateRef, use to render those objects is taken by means of a ContentChild.
<ng-template
ngFor
let-item
[ngForOf]="items"
[ngForTemplate]="template"
[ngForTrackBy]="trackByFunction"
>
</ng-template>
Here is the usage:
<my-component [items]="items">
<ng-template let-item>
{{item.name}}
</ng-template>
</my-component>
I would like to add a directive to each item that would do some simple attribute stuff, like add a class, hold a dimension. The idea is that this component will be used instead ngFor to do more advanced logic. I want to avoid adding the directive to the html on the usage side. I also want to be able to reach this directive from MyComponent.
How can I do this?
Edit:
Even if I’d place the directive on the usage side, I still need a way to query for that rendered items somehow. So far I found no combination of ContentChild/ViewChild and read
that would help here.
The typical is pass the template as input in a component, e.g.
@Component({
selector: 'list-items',
standalone: true,
imports: [CommonModule],
template: `
@if(template)
{
@for (item of items;track $index)
{
<ng-container *ngTemplateOutlet="template;context:{$implicit:item}">
</ng-container>
}
}
`,
})
export class ListItems {
@Input() items: any[] = [];
@Input()template!:TemplateRef<any>;
}
And you use like
<list-items [items]="items" [template]="template"></list-items>
<!--see the ng-template is outside-->
<ng-template #template let-item>
<div style="border:1px solid silver;padding:.5rem;margin:.5rem">
<p>Id:{{item.id}}</p>
<p>Name:{{item.name}}</p>
</div>
</ng-template>
If we can put the template inside like a “ng-content”, we can get the template using ContentChild
@Component({
selector: 'list-items-content',
standalone: true,
imports: [CommonModule],
template: `
@if(templateRef)
{
@for (item of items;track $index)
{
<ng-container *ngTemplateOutlet="templateRef;context:{$implicit:item}">
</ng-container>
}
}
<ng-content></ng-content>
`,
})
export class ListItemsContent {
@Input() items: any[] = [];
@ContentChild(TemplateRef) template!: any;
get templateRef() {
return this.template || null;
}
}
And you use like
<list-items-content [items]="items">
<ng-template let-item>
<div style="border:1px solid silver;padding:.5rem;margin:.5rem">
<p>Id:{{item.id}}</p>
<p>Name:{{item.name}}</p>
</div>
</ng-template>
</list-items-content>
a stackblitz with both options
2