Search code examples
angularng-template

How to use ng-template across different components


How can I render an <ng-template> in different components. Let's say I have component test as the following:

test.html

<ng-template #content >
   <p> Hello world </p>
</ng-template>

test.ts

@Component({
    selector: 'test',
    templateUrl: './test.component.html',
    standalone: true,
})
export class TestComponent implements OnInit {}

Now I want to render it in component A. This is what I've tried (and many other more complicated versions that didn't work, so I keep the question with this simplest version):

A.html:

<test>
<ng-container *ngTemplateOutlet="content"></ng-container>
</test>

The error that I get is :

Property 'content' does not exist on type 'AComponent'.
 <ng-container *ngTemplateOutlet="content"></ng-container>

I think this must be very simple to solve but I've checked the guideline in this post, this solution, and some other tutorials but none of them worked ...


Solution

  • While this is not very common, you can query TemplateRef and use it as any other object - pass it as inputs, and share through services as long as an instance of the component that defines the template exists.

    The most common scenario is passing TemplateRef as input to a child component (it automatically ensures the template exists at the moment the child renders it) which renders it using NgTemplateOutlet directive. Here is an example with the use of template reference variable:

    // parent template
    <ng-template #hello>Hello</ng-template>
    <app-child [template]="hello"></app-child>
    
    // child:
    @Component({
      selector: 'app-child',
      standalone: true,
      imports: [NgTemplateOutlet],
      template: `<ng-template [ngTemplateOutlet]="template"></ng-template>`,
    })
    export class ChildComponent {
      @Input() template?: TemplateRef<any>;
    }
    

    It is commonly used for implementing customizable components that work together and maintain parent-child relationships e.g. menus, tabs, and tree-views. You can find examples of using TemplateRef as input in Material Tabs.


    It is also possible to share it elsewhere as TemplateRef can be accessed in the component via @ViewChild:

    @Component({
      selector: 'app-template-definer',
      standalone: true,
      template: `<ng-template>Hello</ng-template>`,
    })
    export class TemplateDefinerComponent {
      @ViewChild(TemplateRef) template!: TemplateRef<void>;
    }
    

    From here we can pass it to the child component as input or even can share via service. The latter would be considered an unsafe bad practice and has quite a lot of things to take into consideration before implementing this approach.

    Please take a look at Playground which demonstrates how to share with children and globally.