In this blog we will discuss about advanced features of angular core.
ng-container, ng-template and ngTemplateOutlet are angular core directives which on combining together allows the creation of highly dynamical and customizable angular components. Based on these templates, a look of a component can be changed completely like whenever a template is defined it can be instantiated in various places.
1. <ng-template>
<ng-template> is defined as an angular element which is used for rendering HTML. This element is never displayed directly means code inside <ng-template> is not displayed but represented as a comment initially. Let's is with the following example:-
app.component.ts - Parent Component i.e "AppComponent" is defined in this file.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
}
app.component.html -This file contains component view
<ng-template>
<p>Demo Example</p>
</ng-template>
Root module - app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
When above code runs, <ng-template> will be represented as a comment "<!---->"
Output :-
In above output, it can be seen that place of <ng-template> is occupied by comments.
Structural directive, ViewContainerRef, TemplateRef etc are used for displaying <ng-template> element.
Using ng-template with structural directive.
Below we will discuss usage of ng-template with the structural directive like ngFor which is used for repeating a given HTML template once for each value in an array, ngIf used for showing or hiding an element based on condition.
<ng-template> with structural directive ngFor
Following changes are made in above files in order to use <ng-template> with ngFor.
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
students = [
{ name:"Yogesh",stream:"science" },
{ name:"Mithlesh",stream:"commerce" },
{ name:"Anu",stream:"Humanities" },
{ name:"Aditi",stream:"commerce" },
]
}
In above component a simple students array is declared, which will be displayed in component template through ngFor.
app.component.html
<h3>ng-template with directive ngFor</h3>
<ng-template ngFor let-student [ngForOf]= "students" let-i="index">
<p>{{i + 1}}. {{student.name}} : {{student.stream}} </p>
</ng-template>
In above component view, template input variable is declared via let. The properties such as index, first, last, even, odd related to NgFor can be assigned to a variable using let within the <ng-template>.
Root module i.e app.module.ts file remain same.
When above code runs, code within ng-template will be displayed as it is used with the structural directive "ngFor".
Output :-
<ng-template> with conditional structural directive ngIf
Following changes are made to component view i.e app.component.html file
<h3>ng-template with directive ngIf</h3>
<ul *ngFor="let student of students; let i = index">
<ng-template [ngIf]= "student.stream == 'commerce'" ><li>{{student.name}} : {{student.stream}}</li></ng-template>
</ul>
In above code, <ng-template> code is only displayed if certain condition is satisfied i.e only student with stream "commerce" will be displayed.
Remaining files remain same .
Output :-
Using <ng-template> with TemplateRef and ViewContainerRef
This is entirely a new example, in this we are showing that code(which is a simple message) within ng-template can be displayed multiple times using TemplateRef and ViewContainerRef.
Create a directive "MessageDirective" in a file "message.directive.ts"
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[commonMessage]'
})
export class MessageDirective {
constructor(public viewContainerRef:ViewContainerRef) { }
}
It is a simple directive which is used as an attribute selector of an element having ViewContainerRef as dependency injection.
app.component.html
<h3>ng-template example with TemplateRef and ViewContainerRef</h3>
<ng-template #commonmsg>
Welcome to Angular learning.<br/>
</ng-template>
<h3>Message </h3>
<div commonMsg> </div>
<h3>Message </h3>
<div commonMsg> </div>
Template reference variable "commonmsg" is assigned to ng-template. MessageDirective is being used with attribute selector directive "commonMsg" in various div element.
app.component.ts
import { Component,ViewChild,ViewChildren,TemplateRef,QueryList,AfterViewInit } from '@angular/core';
import { MessageDirective } from './message.directive';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent implements AfterViewInit {
@ViewChild('commonmsg') private commonMsgTempRef : TemplateRef<any>
@ViewChildren(MessageDirective) private messageDirectiveQueryList: QueryList<MessageDirective>
ngAfterViewInit() {
this.messageDirectiveQueryList.map(messageDirective =>
messageDirective.viewContainerRef.createEmbeddedView(this.commonMsgTempRef));
}
}
In above code <ng-template> reference is accessed as TemplateRef via ViewChild. Here a view container reference is provided by directive. Therefore all view container references provided by directive will be fetched using @ViewChildren() and QueryList. Finally template reference is embedded to all view container references. In short data obtained via template reference variable is embedded to elements wherever message directive is used.
Output :-
2. <ng-container>
ng-container find it's usage when multiple structural directives are to be used together. Let's consider an example where we are using multiple structural directives :-
app.component.html
<ul *ngFor="let student of students; let i = index" *ngIf= "students">
<li>{{student.name}} : {{student.stream}}</li>
</ul>
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
students = [
{ name:"Yogesh",stream:"science" },
{ name:"Mithlesh",stream:"commerce" },
{ name:"Anu",stream:"Humanities" },
{ name:"Aditi",stream:"commerce" },
]
}
Above code will not work and give following error :-
This shows that two structural directives cannot be used together to the same element. To make above code work following changes is to be made :-
app.component.html
<div *ngIf= "students">
<ul *ngFor="let student of students; let i = index">
<li>{{student.name}} : {{student.stream}}</li>
</ul>
</div>
ngIf directive is moved to outer div element thereby creating an extra div element. To avoid such scenario angular provided <ng-container> to apply multiple structural directive without creating an extra div element.
app.component.html
<ng-container *ngIf= "students">
<ul *ngFor="let student of students; let i = index">
<li>{{student.name}} : {{student.stream}}</li>
</ul>
</ng-container>
3. ngTemplateOutlet
An embedded view from a prepared TemplateRef can be inserted via ngTemplateOutlet. Let's see an example:
app.component.html
<ng-template #studentlist>
<ul *ngFor="let student of students; let i = index">
<li>{{student.name}} : {{student.stream}}</li>
</ul>
</ng-template>
<ng-container *ngTemplateOutlet="studentlist"></ng-container>
The template being loaded is refered via template reference "#studentlist" and ngTemplateOutlet is used to instantiate the template.
0 Comment(s)