Search code examples
angularangular-dynamic-components

Angular Dynamic Components: @ViewChildren get ViewContainerRef for every component in QueryList


I am building a dialog with dynamic tabs that can receive a component to be placed in the tab body. I am having a hard time creating multiple dynamic components using @ViewChildren. I have successfully done this with a single component and @ViewChild in the past quite easily.

Here is my template:

<mat-tab *ngFor="let tab of tabs" [label]="tab.label">
     <ng-template #comp></ng-template>
</mat-tab>

Here is my component logic:

@ViewChildren("comp") dynComponents: QueryList<any>;


public ngAfterContentInit() {
  this.tabs.forEach(tab => {
     const factory = this._resolver.resolveComponentFactory(tab.component);
     console.log(this.dynComponents); // Returns undefined.

     // this.componentRef = this.vcRef.createComponent(factory);
  });
}

My dynComponents are undefined even when hard-coding components in the Template. I need to seemingly get the ViewContainerRef from this dynComponents QueryList, but I am not sure why it is not populating at all. I used this post for reference: Post


Solution

  • The @ViewChildren in the component is not working because it is missing the read metadata property indicating ViewContainerRef.

    Component

    import {
      AfterContentInit, Component, ComponentFactoryResolver, QueryList, Type, ViewChildren, ViewContainerRef
    } from '@angular/core';
    
    
    @Component({
      selector: 'dynamic-dialog',
      templateUrl: './dynamic-dialog.component.html',
      styleUrls: ['./dynamic-dialog.component.scss']
    })
    export class DynamicDialogComponent implements AfterContentInit {
      @ViewChildren('comp', { read: ViewContainerRef })
      public dynComponents: QueryList<ViewContainerRef>;
    
      public tabs = [];
    
      constructor(private _resolver: ComponentFactoryResolver) {}
    
      ngAfterContentInit() {
        this.dynComponents.map(
          (vcr: ViewContainerRef, index: number) => {
            const factory = this._resolver.resolveComponentFactory(
              this.tabs[index].component);
            vcr.createComponent(factory);
          }
        )
      }
    }
    

    p.s. Dynamic content may be loaded using lifecycle hook AfterContentInit or AfterViewInit.