Paul Féraud
Paul Féraud

Reputation: 113

Creating/Fetching a TemplateRef from within a directive

I’m trying to create a (structural) directive that inserts a TemplateRef, but where the TemplateRef is defined "somewhere else".

Context

I sometimes want to insert arbitrary content into an existing element, but for DOM reasons, it’s important that it not be a component (though an attribute-like component is fine).

Example:

<table>
  <tr my-row-component></tr>
</table>
@Component({
  selector: 'tr[my-row-component]'
  template: `<td>...</td><td>...</td><td>...</td>...`
})

Now from here, I’d want to do the same thing, but insert 2 rows into my table. So I was hoping to do something like:

<table>
  <ng-template myTwoRowsDirective></ng-template>
</table>

The issue is that:

Question

How could I get a TemplateRef inside a Structural directive, but that is not passed in by the caller of the directive?

@Directive({selector: '[myTwoRowsDirective]'})
export class MyTwoRowsDirective {
  constructor(
      viewContainerRef: ViewContainerRef) {
    const templateRef = ???; // Reference to template defined elswhere
    viewContainerRef.createEmbeddedView(templateRef, this.context);
  }
}

Upvotes: 2

Views: 2619

Answers (1)

David Bult&#233;
David Bult&#233;

Reputation: 3109

Don't know if this is a recommended practice, but this seems to work (haven't tested it on your use case though):

@Component({
  template: `
    <ng-template #helloRef>
      <h1>hello</h1>
    </ng-template>
  `
})
export class TemplatesComponent {
  @ViewChild('helloRef', { static: true }) public helloRef: TemplateRef<any>;
}

@Directive({
  selector: 'whatever-component'
})
export class CustomizeWhateverDirective implements AfterViewInit {
  private static componentRef: ComponentRef<TemplatesComponent>;

  constructor(
    @Self() private whatever: WhateverComponent,
    private resolver: ComponentFactoryResolver,
    private _vcr: ViewContainerRef
  ) {}

  ngAfterViewInit(): void {
    const componentRef = this.getComponentRef();

    const helloRef = componentRef.instance.helloRef;
    this.whatever.helloTemplate = helloRef;
  }

  private getComponentRef() {
    if (!CustomizeWhateverDirective.componentRef) {
      const factory = this.resolver.resolveComponentFactory(TemplatesComponent);
      CustomizeWhateverDirective.componentRef = this._vcr.createComponent(factory);
    }

    return CustomizeWhateverDirective.componentRef;
  }
}

This code sets the helloTemplate attribute of all Whatever components in my project.

So the trick is to have a component with the templateRef (TemplatesComponent in the example), and create that component (through viewContainerRef.createComponent) and access the templateRef.

Upvotes: 4

Related Questions