Angular ng-template, ng-container and ngTemplateOutlet

The ng-template Directive

  • Like the name specify, the ng-template directive describes an Angular template. This means that the content of this tag will carry part of a template, that can then be controlled together with other templates to form the final component template.
  • The Angular framework is already using ng-template under the hood in many of the structural directives that we use many times in angular project: 

ngIfngFor and ngSwitch.

  • Now, we started learning ng-template with an example. And here we are defining two tab buttons login and signup:
@Component({
  selector: 'app-root',
  template: `      
      <ng-template>
          <button class="tab-button" 
                  (click)="login()">{{loginMsg}}</button>
          <button class="tab-button" 
                  (click)="signUp()">{{signUpMsg}}</button>
      </ng-template>
  `})
export class AppComponent {
    loginMsg = 'Login Page';
    signUpMsg = 'Sign Up Page'; 
    lesson = ['Lesson 1', 'Lessons 2'];

    login() {
        console.log('Login Page');
    }
    signUp() {
        console.log('Sign Up Page');
    }
}
  • When you execute the above example, you might be surprised to find out that the above example does not render anything to the screen.
  • It has normal and expected actions. This is by reason of with the ng-template tag we are directly explaining a template, but we are not using it yet.
  • Let’s see and find an example where we can render an output, using some of the most commonly used angular directives.
  • The ng-if and ng-template directive
<div class="lesson-list" *ngIf="lesson else loading">
  ... 
</div>

<ng-template #loading>
    <div>Loading...</div>
</ng-template>
  • In this example, we implement ng-template with if/else.
  • A very common use of the ngIf/else functionality: we display an alternative loading template while waiting for data to arrive from the back end.

Multiple Structural Directive in Angular

  • Now, let’s see what happens if we use ngIf and ngFor in the same element:
<div class="lesson" *ngIf="lesson1" 
       *ngFor="let lesson of lesson1">
    <div class="lesson-detail">
        {{lesson | json}}
    </div>
</div> 
  • This is not working and we get errors. Because that is not possible to apply two structural directives in the same element.
  • Now we apply the below code:
<div *ngIf="lesson1">
  <div class="lesson" *ngFor="let lesson of lesson1">
      <div class="lesson-detail">
          {{lesson | json}}
      </div>
  </div>
</div>
  • In this example, we use both structural directives in different div.

ng-container Directive

  • To avoid having to create an extra div, so we can use the ng-container directive:
<ng-container *ngIf="lesson1">
  <div class="lesson" *ngFor="let lesson of lesson1">
      <div class="lesson-detail">
          {{lesson | json}}
      </div>
  </div>
</ng-container>
  • The ng-container provides us with an element that we have to attach a structural directive to a section of the page, without creating any extra element just for that.
  • The ng-container directive is mostly used as a placeholder for dynamically injecting content into the page.

The ngTemplateOutlet Directive in Dynamic Template Creation

  • It is able to create template mentions and point them to other directives like ngif is just the beginning.
  • We can also take the template itself and abstract it anywhere on the page, and use the ngTemplateOutlet directive:
<ng-container *ngTemplateOutlet="loadingPage"></ng-container>
  • As you see above how ng-container helps with this use case: in the above code, we are using it to instantiate on the page the loadingPage template that we define in the above example.
  • We are mentioning to loading template via its template reference #loadingPage, and we are using the ngTemplateOutlet structural directive to abstract the template.
  • We can add a number of ngTemplateOutlet tags to the page as we would like, and instantiate many different templates.

Template Context

  • In the ng-template tag body, we access to same context variables that are visible in the outer template, for example, the variable lessons.
  • Each template can also define its own set of input variables, and each template has connected a context object holding all the template-specific input variables.
  • Now see in example:
@Component({
  selector: 'app-root',
  template: `      
<ng-template #estimateTemplate let-lessonCounter="estimate">
    <div> Approximately {{lessonCounter}} lesson ...</div>
</ng-template>
<ng-container 
   *ngTemplateOutlet="estimateTemplate;context:ctx">
</ng-container>
`})
export class AppComponent {

    totalEstimate = 10;
    ctx = {estimate: this.totalEstimate};
  
}
  • The input variable is called lessonCounter, and it is defined via an ng-template property with the prefix of let-
  • The lessonCounter variable is visible in the ng-template body, and it is not visible outside of the ng-template body.
  • The context object must have a property named estimate, for any value to display inside the template. 

Template References

  • There is same way that we can mention to loading template using template reference, and we can also have a template directly into our component using the ViewChild:
@Component({
  selector: 'app-root',
  template: `      
      <ng-template #TabButtons>
          <button class="tab-button" (click)="login()">
            {{loginText}}
          </button>
          <button class="tab-button" (click)="signUp()">
            {{signUpText}}
          </button>
      </ng-template>
`})
export class AppComponent implements OnInit {

    @ViewChild('TabButtons')
    private TabButtonsTpl: TemplateRef<any>;
    ngOnInit() {
        console.log(this.TabButtonsTpl);
    }
}
  • In an example of why we want to create a more personalized component, in this component, we pass a configuration parameter, configuration object, or a template as an input parameter.
  • The templates are obtainable also at the level of the component class, and we can do things for example pass them to child components. With all these components,  Angular offers fine building accessibility enabling flawless performance.

Configurable Component with Template Partial @inputs:

  • Now we take for example a tab container, where we like to give the user of the component the     chance of configuring the look and feel of the tab buttons.
  • Here, we would start by defining the custom template for buttons in the parent component:
@Component({
  selector: 'app-root',
  template: `      
<ng-template #customTabBtns>
    <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]="customTabBtns"></tab-container>      
`})
export class AppComponent implements OnInit {

}
  • And then we create tab container component:
@Component({
  selector: 'tab-container',
  template: `
  
<ng-template #defaultTabBtns>
  
  <div class="default-tab-buttons">
      ...
  </div>
  
</ng-template>
<ng-container 
*ngTemplateOutlet="headerTemplate ? headerTemplate: defaultTabBtns">
  
</ng-container>
... rest of tab container component ...
`})
export class TabContainerComponent {
  @Input()
  headerTemplate: TemplateRef<any>;
}
  • In example defaultTabBtns is for the default template for the tab buttons.
  • The defaultTabBtns template used only if input property headerTemplate remains undefined.
  • The result of this design is that the tab container will display the default look.

Conclusion

The core directives ng-container, ng-template, and ngTemplateOutlet combine to create highly dynamic and personalize components. We can even change totally the look and feel of a component based on input templates, and we can also define a template and instantiate it on multiple places of the application.

A seasoned technocrat using Microsoft technology to design technical solutions for a variety of industries. With a sharp understanding and technical acumen, he has delivered hundreds of Web, Cloud, Desktop, and Mobile solutions and is heading the technical department at an esteemed Word Add-in Development Company – iFour Technolab Pvt. Ltd.

Leave a Reply

Your email address will not be published.