angularng-contentangular-content-projection

Programmatically pass on projected content to children? (that would be rendered into a <ng-content> otherwise)


Say, I have a <hook> component that creates a child programmatically, how could I pass the content (that Angular would render into a <ng-content></ng-content> in hook's template) as ng-content to that child component instead (that may or may not decide to display it)?

<hook ...>
   Dynamic content should be passed to hook's programmatically created child
</hook>

I found a very helpful explanation about content projection, that shows how to pass projected content to a programmatically created component, which is one half of my problem. The missing link for me is still: How to access the content passed to hook to pass it on.


Solution

  • If I fully understood the problem, here could be a solution:

    app.component.ts

    @Component({
      selector: 'my-app',
      template: `
        <h1>App comp</h1>
    
        <hook>
          awesome content here
        </hook>
      `
    })
    export class AppComponent  { }
    

    hook.component.ts

    @Component({
      selector: 'hook',
      template: `
        <h2>Hook comp</h2>
    
      <ng-template #content>
        <ng-content></ng-content>
      </ng-template>
    
      <ng-container #vc></ng-container>
      `
    })
    export class HookComp {
      @ViewChild('content', { static: true, read: TemplateRef })
      contentTpl: TemplateRef<any>;
      @ViewChild('vc', { static: true, read: ViewContainerRef })
      vc: ViewContainerRef;
    
      constructor (
        private cfr: ComponentFactoryResolver,
        private injector: Injector,
      ) { }
    
      ngAfterViewInit () {
        this.createChildComp();
      }
    
      private createChildComp () {
        const compFactory = this.cfr.resolveComponentFactory(HookChildComp);
        const componentRef = this.vc.createComponent(compFactory);
    
        componentRef.instance.contentTpl = this.contentTpl;
    
        componentRef.changeDetectorRef.detectChanges();
      }
    }
    

    hook-child.component.ts

    @Component({
      selector: 'hook-child',
      template: `
        <h3>Hook child comp</h3>
    
        <ng-container *ngTemplateOutlet="contentTpl"></ng-container>
      `
    })
    export class HookChildComp {
      contentTpl: TemplateRef<any>;
    }
    

    As you can see, I'm able to grab the hook's ng-content by wrapping it into an ng-template. Then, I can simply query for that template and pass it to the programmatically created child.