javascriptangulardirectiveng-template

What are practical scenarios of *ngTemplateOutlet directive?


I was reading about *ngTemplateOutlet directive. The use of this directive is to instantiate a template dynamically by a template reference and context object as parameters.

What I want to know is that we have so many things in Angular to achieve the same results as *ngTemplateOutlet such as:

  1. We can have multiple *ngIf which could render different templates based on the component variable value within the same component. In a similar fashion we have [ngSwitch] which would render different templates for us based on different values.

  2. We could use references with *ngIf by referring to the template reference variable of the respective variable.

For the former case:

<div *ngIf="condition1"> Content 1 </div>
<div *ngIf="condition2"> Content 2 </div>
<div *ngIf="condition3"> Content 3 </div>

And for latter:

<ng-container *ngIf="condition then myTemplate else otherTemplate"></ng-container>
<ng-template #myTemplate> Some content... </ng-template>
<ng-template #otherTemplate> Some content... </ng-template>

If we have such methods in our arsenal what more value does *ngTemplateOutlet add?

What are the practical use cases (if there are any) where we cannot use the above methods and should use *ngTemplateOutlet directive or is it just another method to choose from to achieve the same result?


Solution

  • Angular template outlets can be used to insert a common template in various sections of a view that are not generated by a loop or subject to a condition. For example, you can define a template for the logo of a company and insert it in several places in the page:

    <div>
      <ng-container *ngTemplateOutlet="companyLogoTemplate"></ng-container>
      <h1>Company History</h1>
      <div>{{companyHistory}}</div>
    </div>
    <form (ngSubmit)="onSubmit()">
      <ng-container *ngTemplateOutlet="companyLogoTemplate"></ng-container>
      <h1>User info</h1>
      <label>Name:</label><input type="text" [(ngModel)]="userName" />
      <label>Account ID:</label><input type="text" [(ngModel)]="accountId" />
      <button>Submit</button>
    </form>
    <div class="footer">
      <ng-container *ngTemplateOutlet="companyLogoTemplate"></ng-container>
    </div>
    
    <ng-template #companyLogoTemplate>
      <div class="companyLogo">
        <img [src]="logoSourceUrl">
        <label>The ACME company, {{employeeCount}} people working for you!</label>
      </div>
    </ng-template>
    

    Templates and template outlets can also help to make a component configurable. The following example is taken from this article by Angular University.

    A tab container component defines a default tab header template, but allows to override it with a custom template defined as an input property. The appropriate template (default or custom) is then inserted in the view with a template outlet:

    @Component({
      selector: 'tab-container',
      template: `
        <ng-template #defaultTabButtons>
          <div class="default-tab-buttons">
            ...
          </div>
        </ng-template>
        <ng-container *ngTemplateOutlet="headerTemplate || defaultTabButtons"></ng-container>
        ... rest of tab container component ...
      `
    })
    export class TabContainerComponent {
        @Input() headerTemplate: TemplateRef<any>; // Custom template provided by parent
    }
    

    In the parent component, you define the custom tab header template and pass it to the tab container component:

    @Component({
      selector: 'app-root',
      template: `      
        <ng-template #customTabButtons>
          <div class="custom-class">
            <button class="tab-button" (click)="login()">
              {{loginText}}
            </button>
            <button class="tab-button" (click)="signUp()">
              {{signUpText}}
            </button>
          </div>
        </ng-template>
        <tab-container [headerTemplate]="customTabButtons"></tab-container>      
      `
    })
    export class AppComponent implements OnInit {
      ...
    }
    

    You can see another advanced use case in this blog post by alligator.io.