angularangular-content-projection

Is there an equivalent to `TemplateRef` for Angular components, for conditional/dynamic content projection?


I would like to be able to have this projected content:

<app-host>
  <app-some-compA tag>...</app-some-compA>
  <app-some-compB tag>...</app-some-compB>
</app-host>

And then use app-host ViewContainerRef to viewContainerRef.createComponent() as many component A's and B's as I want. Is there an equivalent ComponentRef to the TemplateRef that I could use to read in my content children query? Using template refs I could solve it like this:

class AppHostComponent {
  @ContentChildren(TagDirective, {read: TemplateRef}) tagged: QueryList<TemplateRef>;
  constructor(private readonly container: ViewContainerRef) {}

  createNew() {
    this.container.createEmbeddedView(this.tagged.get(0));
  }
}

this is ofcourse just a minimal example, rough around the edges, but I'd like to do the same with components, instead of templates, but I don't know what I could read in the content query to use?


Solution

  • No, ComponentRef is already a created instance ref, BUT you can use template refs for your purpose.

    Angular star syntax *someDirective already creates a template implicitly. so you could do

    @Directive({selector: '[itemTpl]'})
    class ItemTplDirective { // helper directive to mark and reference the template
      tpl = inject(TemplateRef);
    }
    
    class AppHostComponent {
      @ContentChildren(ItemTplDirective) tagged: QueryList<ItemTplDirective>;
      constructor(private readonly container: ViewContainerRef) {}
    
      createNew() {
        this.container.createEmbeddedView(this.tagged.get(0).tpl);
      }
    }
    
    <app-host>
      <app-some-compA *itemTpl>...</app-some-compA>
    </app-host>
    

    or a more simplistic (but less powerful) api would be to pass a component class as a property and use .createComponent instead of .createEmbeddedView

    <app-host [components]="components"/>
    ...
    components = [ComponentAClass, ComponentBClass]