What are practical scenarios of *ngTemplateOutlet directive?

patrick.1729 picture patrick.1729 · Oct 20, 2018 · Viewed 21.3k times · Source

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?

Answer

ConnorsFan picture ConnorsFan · Oct 21, 2018

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.