Search code examples
angularangular-content-projection

Angular: Content projection access data of component


I've a test component that receives an array of objects via @Input(). I want to modify the data according to some internal logic / extra buttons but also allow the caller to decide how the data should be displayed.

Example of the component, with a trivial .toUpperCase() to make user names uppercase:

@Component({
  selector: 'test-component',
  template: `
    <h1>My Test</h1>
    <ng-content></ng-content>
  `
})
export class TestComponent implements OnInit {

  @Input() data?: Array<{ name: string }>;

  ngOnInit(): void {
     this.data.map((item: any) => {
        item.name = item.name.toUpperCase();
        return item;
     });
  }

Now is there a way I can, in the projected content, access the data that was provided to the component? Something like this:

<test-component [data]="[{name: 'joe'}, {name: 'mike'}]">
    <p *ngFor="let person of data">{{ person.name }}</p>
</test-component>

This would be very helpful to, for example, build a component where I could project a table and use *ngFor to render rows. The component itself would add pagination buttons and filter the data used by the *ngFor accordingly.


Solution

  • Here is a solution for the problem.

    Component:

    @Component({
      selector: 'test-component',
      template: `
        <h1>My Test</h1>
        <ng-container
            [ngTemplateOutlet]="template"
            [ngTemplateOutletContext]=" {
               data: this.data
            }"
          ></ng-container>
      `
    })
    export class TestComponent implements OnInit {
    
      @Input() data?: Array<{ name: string }>;
      @ContentChild(TemplateRef) template: TemplateRef<any> | null = null;
    
      ngOnInit(): void {
         this.data.map((item: any) => {
            item.name = item.name.toUpperCase();
            return item;
         });
      }
    

    Usage:

    <test-component [data]="[{name: 'joe'}, {name: 'mike'}]">
        <ng-template let-data="data">
            <p *ngFor="let person of data">{{ person.name }}</p>
        </ng-template>
    </test-component>