angulartransclusionangular2-ngcontent

Angular 9+: nested ng-content and ContentChildren


I'm trying to capture the content of an outer ng-content with ContentChildren, with no luck. Here's the code: app.component.html:

<app-table-wrapper>
    <app-column></app-column>
    <app-column></app-column>
    <app-column></app-column>
</app-table-wrapper>

table-wrapper:

@Component({
  selector: 'app-table-wrapper',
  template: `<p>table-wrapper works!</p>
    <app-hyd-table>
      <ng-content></ng-content>
    </app-hyd-table> `,
})
export class TableWrapperComponent implements OnInit {
  @ViewChild(HydTableComponent, { static: true }) _hydTable: HydTableComponent;
  @ContentChildren(ColumnComponent, { descendants: true }) columns: QueryList<
    ColumnComponent
  >;

  constructor() {}

  ngOnInit(): void {
    console.log('table wrapper - ngOnInit');
  }

  ngAfterContentInit(): void {
    console.log('table wrapper - ngAfterContentInit');
    console.log({ columns: this.columns });
  }

  ngAfterViewInit(): void {
    console.log('table wrapper - ngAfterViewInit');
    this._hydTable.columns = this.columns;  // <--- Manual override
  }
}

table:

@Component({
  selector: 'app-hyd-table',
  template: `<p>hyd-table works!</p>
    <ng-content></ng-content>
    <div *ngFor="let col of columns; let i = index">Column {{ i }}</div>
  `,
})
export class HydTableComponent implements OnInit {
  @ContentChildren(ColumnComponent) columns: QueryList<ColumnComponent>; // <-- this is empty
  
  constructor() {}

  ngOnInit(): void {
      console.log("table - ngOnInit");
  }

  ngAfterContentInit(): void {
      console.log("table - ngAfterContentInit");
      console.log({ columns: this.columns });
  }

  ngAfterViewInit(): void {
      console.log("table - ngAfterViewInit");
  }
}

In the table component, the QueryList is empty and I have to set it from the table-wrapper component manually in ngAfterViewInit, otherwise the *ngFor in the table component would not render anything.

So my question is: how do I "forward" the ng-content of the wrapper component to the inner component's ContentChildren? The HTML is even rendered inside the inner component, it's just the ContentChildren that fails to pick up the columns inside its template.


Solution

  • After finding the right keywords to use in Google, I found out that this issue has been open for 3+ years now and still does not have a solution: https://github.com/angular/angular/issues/16299 Seems like I'll have to do it the dirty way.