Search code examples
angulariterationangular-content-projection

Angular Content Projection with iteration


myComponent.html - <my-component>

<ng-container *ngFor="let item of dataProvider">
    <ng-template *ngTemplateOutlet="tmpContent; context: {$implicit: item, item: item}">
    </ng-template>
</ng-container>

<ng-template #tmpContent let-item="item">
    <div>
        {{item.id}}
        <ng-content></ng-content>
    </div>
</ng-template>

And the usage:

 <my-component dataProvider='source'>
    <ion-label> test </ion-label>
 </my-component>

It seems iterate the correct number of times, but displays the <ion-label>, which is content projected only once in the last iteration. So is it possible to content project this ion-label every time. I need to be implemented with content projection as instead of ion-label could be anything, so will be passed in declarative way.


Solution

  • You can pass a the label as a template and then reuse the template inside your component. In the component add a new input called labelTemplate that is passed a TemplateRef. Then in the view, you can reference it like any template - I assumed you wanted it nested in the template you provided in your example, but there is no reason it couldn't be in the main for loop block.

    export class MyComponentComponent {
      /** The template used to display the label */
      @Input() labelTemplate?: TemplateRef<{ $implicit: unknown }>;
    }
    
    <ng-template #tmpContent let-item="item">
      <div>
        {{item.id}}
        <ng-template *ngTemplateOutlet="labelTemplate; context: {$implicit: item.name }" />
      </div>
    </ng-template>
    

    In the consuming component's html template, add a label template anywhere within the markup - it doesn't have to be projected. The reference to the template is passed to the labelTemplate property attribute on the my-component element.

    <my-component dataProvider='source' [labelTemplate]="label">
      <ng-template #label let-content>
        <ion-label> test </ion-label>
      </ng-template>
    </my-component>