angularchildrenng-content

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

  • 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-container>
    </div>