Angular Custom Structural Directive Example
January 05, 2021
This page will walk through Angular custom structural directive example. Angular provides three types of directive: component directive, attribute directive and structural directive. Component directive is used to create HTML template. This is most commonly used directive in Angular project. Attribute directive changes the appearance or behavior of DOM element. Structural directive is used to change the DOM layout by adding and removing DOM elements. Angular provides in-built attribute directive such as NgStyle
. Angular also provides in-built structural directive such as NgFor
and NgIf
. We can create custom attribute directives and custom structural directives using @Directive
decorator. On this page we will learn how to create custom structural directive. Structural directives are responsible for HTML layout. We can add and remove elements in DOM layout dynamically. The HTML element using directive is called host element for that directive. The effect of directive is only on host element and its descendants. To add and remove host elements from DOM layout we can use TemplateRef
and ViewContainerRef
classes in our structural directive. Now find the complete example step by step.
Contents
Technologies Used
Find the technologies being used in our example.1. Angular 11.0.3
2. Node.js 12.5.0
3. NPM 6.9.0
Project Structure
Find the project structure.angular-demo | |--src | | | |--app | | | | | |--directives | | | | | | | |--cp-delay.directive.ts | | | |--cp-if.directive.ts | | | |--cp-loop.directive.ts | | | | | |--app.component.ts | | |--app.component.html | | |--app.module.ts | | | |--main.ts | |--index.html | |--styles.css | |--node_modules |--package.json
Steps to Create Custom Structural Directive
Find the steps to create custom structural directive.Step-1: Create a class decorated with
@Directive
. Using selector
metadata we will give a name to our directive. The directive name will be enclosed with bracket [ ] such as [cpIf]. Find the example.
@Directive({ selector: '[cpIf]' }) export class CpIfDirective { }
cpIf
. Custom directive name should not be Angular keyword, it should also not start with ng
. We should use our own keyword such as company name as prefix in the nomenclature of our custom directive.
Step-2: Create a setter method decorated with
@Input()
. We need to take care that the method name should be same as directive name. Find the code snippet.
@Input() set cpIf(condition: boolean) { }
@Input()
alias should be same as directive name. Find the code snippet.
@Input('cpIf') set myCpIf(condition: boolean) { }
import { CpIfDirective } from './directives/cp-if.directive.ts'; @NgModule({ ---------------- ---------------- declarations: [ --------- --------- CpIfDirective --------- --------- ] ---------------- ---------------- }) export class AppModule { }
NgFor
and NgIf
. We can use structural directive either with *
prefix in host element or by using ng-template
. Using structural directive with *
is preferable because it is considered more readable. Now find the code snippet how to use structural directive.
a. Using directive with
*
prefix in host element.
<div *cpIf="showCpIf"> <b>Hello cpIf Directive.</b> </div>
ng-template
.
<ng-template [cpIf]="showCpIf"> <div> <b>Hello cpIf Directive.</b> </div> </ng-template>
TemplateRef and ViewContainerRef
To change DOM layout we should useTemplateRef
and ViewContainerRef
in our structural directive.
TemplateRef : It represents an embedded template that can be used to instantiate embedded views.
ViewContainerRef: It represents a container where one or more views can be attached.
To use the above classes in our directive, first we need to instantiate them. Instantiate these classes using dependency injection in constructor as following.
constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { }
createEmbeddedView()
method of ViewContainerRef
. Find the line of code.
this.viewContainer.createEmbeddedView(this.templateRef);
clear()
method of ViewContainerRef
as given below.
this.viewContainer.clear();
Create Directive for If Condition
We will create a custom structural directive that will take boolean argument and will work likeNgIf
directive. When we will pass true, then the host element and its descendants will be added in DOM layout and if we pass false then host element and its descendants will be removed from DOM layout. Find the code.
cp-if.directive.ts
import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core'; @Directive({ selector: '[cpIf]' }) export class CpIfDirective { constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { } @Input() set cpIf(condition: boolean) { if (condition) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } } }
cpIf
. Find the sample example to use it.
<div> <input type="radio" name="rad1" (click)= "showCpIf= true"> True <input type="radio" name="rad1" (click)= "showCpIf= false"> False </div> <br/> <div *cpIf="showCpIf"> <b>Hello cpIf Directive.</b> </div> <ng-template [cpIf]="!showCpIf"> <div> <b>Message not Available.</b> </div> </ng-template>
Create Directive for Loop
We will create a custom structural directive that will create host element as many times as given by user in DOM layout. Find the code.cp-loop.directive.ts
import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core'; @Directive({ selector: '[cpLoop]' }) export class CpLoopDecorator { constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { } @Input('cpLoop') set loop(num: number) { for(var i=0; i < num; i++) { this.viewContainer.createEmbeddedView(this.templateRef); } } }
cpLoop
and we can use it as given below.
<ul> <li *cpLoop="5" > Hello World! </li> </ul>
Create Directive with setTimeout()
We will create a custom structural directive that will add host element in DOM layout after a given timeout. Find the code.cp-delay.directive.ts
import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core'; @Directive({ selector: '[cpDelay]' }) export class CpDelayDirective { constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { } @Input() set cpDelay(delay: number) { this.viewContainer.clear(); setTimeout(() => { this.viewContainer.createEmbeddedView(this.templateRef); }, delay); } }
cpDelay
and we can use it as given below.
<div> Show message after <input type="radio" name="rad2" (click)= "showCpDelay= true; delayInSec = 2000"> 2 Seconds <input type="radio" name="rad2" (click)= "showCpDelay= true; delayInSec = 5000"> 5 Seconds <input type="radio" name="rad2" (click)= "showCpDelay= true; delayInSec = 7000"> 7 Seconds </div> <br/> <div *cpIf="showCpDelay"> <div *cpDelay="delayInSec"> <b> Hello cpDelay Directive. </b> </div> </div>
Application Component and Module
app.component.tsimport { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent { showCpIf = false; showCpDelay = false; delayInSec = 0; }
<h3>cpIf Directive</h3> <div> <input type="radio" name="rad1" (click)= "showCpIf= true"> True <input type="radio" name="rad1" (click)= "showCpIf= false"> False </div> <br/> <div *cpIf="showCpIf"> <b>Hello cpIf Directive.</b> </div> <ng-template [cpIf]="!showCpIf"> <div> <b>Message not Available.</b> </div> </ng-template> <h3>cpLoop Directive</h3> <ul> <li *cpLoop="5" > Hello World! </li> </ul> <h3>cpDelay Directive</h3> <div> Show message after <input type="radio" name="rad2" (click)= "showCpDelay= true; delayInSec = 2000"> 2 Seconds <input type="radio" name="rad2" (click)= "showCpDelay= true; delayInSec = 5000"> 5 Seconds <input type="radio" name="rad2" (click)= "showCpDelay= true; delayInSec = 7000"> 7 Seconds </div> <br/> <div *cpIf="showCpDelay"> <div *cpDelay="delayInSec"> <b> Hello cpDelay Directive. </b> </div> </div>
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { CpIfDirective } from './directives/cp-if.directive'; import { CpDelayDirective } from './directives/cp-delay.directive'; import { CpLoopDecorator } from './directives/cp-loop.directive'; @NgModule({ imports: [ BrowserModule, FormsModule ], declarations: [ AppComponent, CpIfDirective, CpDelayDirective, CpLoopDecorator ], bootstrap: [ AppComponent ] }) export class AppModule { }
Run Application
To run the application, find the steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200
Find the print screen of the output.
