'In Angular, how to do Content Projection over the list of children?
I have a component that should accept arbitrary amount of child components of known type and render them with additional wrapper in an *ngFor. Alternatively, can I pass and render a list of <ng-template>?
Container will be used like this:
<app-custom-container>
<app-custom-child name="1">...</app-custom-child>
<app-custom-child name="2">...</app-custom-child>
<app-custom-child name="3">...</app-custom-child>
</app-custom-container>
Inside container template I'd like to do something like this:
template: '<div>
<ng-container *ngFor="let child of ...?">
<app-custom-child-internal-wrapper [id]="child.id">
<ng-content/> <!--somehow only render this specific child-->
</app-custom-child-internal-wrapper>
</ng-content>
</div>'
Can this or something similar be done?
Solution 1:[1]
this is a use case for ContentChildren and ngTemplateOutlet... like so
first you need a directive to mark and get a template ref of any children you want rendered:
@Directive({ selector: '[childMarker]'})
export class ChildMarkerDirective {
constructor(public templateRef: TemplateRef<any>) { }
}
then add it to the children:
<app-custom-container>
<app-custom-child *childMarker name="1">...</app-custom-child>
<app-custom-child *childMarker name="2">...</app-custom-child>
<app-custom-child *childMarker name="3">...</app-custom-child>
</app-custom-container>
in your custom container controller, you need something like this to query the marker in the content:
@ContentChildren(ChildMarkerDirective)
children
then in your template, you can iterate and project:
<div>
<ng-container *ngFor="let child of children">
<app-custom-child-internal-wrapper>
<ng-container *ngTemplateOutlet="child.templateRef"></ng-container>
</app-custom-child-internal-wrapper>
</ng-content>
</div>
Solution 2:[2]
Solved it. You can use CustomChildComponent for template only and get a list if them with @ContentChildren(CustomChildComponent)
Usage:
<app-custom-container>
<app-custom-child name="1">
<ng-template #innertemplate>
...
</ng-temlate>
</app-custom-child>
<app-custom-child name="2">
<ng-template #innertemplate>
...
</ng-temlate>
</app-custom-child>
</app-custom-container>
Child component:
@Component({
selector: 'app-custom-child',
template: '', // template empty by design
})
export class CustomChildComponent {
@Input() public name!: string;
@ContentChild('innertemplate', { read: TemplateRef })
public innerTemplateReference!: TemplateRef<any>;
}
Parent component:
@Component({
selector: 'app-custom-container',
template: '<app-some-internal-container>
<ng-container *ngFor="let child of contentChildren">
<app-some-internal-wrapper>
<ng-container ngTemplateOutlet="child.innerTemplateReference">
</ng-container>
</app-some-internal-wrapper>
</ng-container>
</app-some-internal-container>',
})
export class CustomContainerComponent {
@ContentChildren(CustomChildComponent)
public contentChildren: QueryList<CustomChildComponent>;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
