Angular Custom Structural Directive Example

By Arvind Rai, February 11, 2024
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.

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 { } 
In the above code the custom directive name is 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) { 
} 
If we want different method name then @Input() alias should be same as directive name. Find the code snippet.
 
@Input('cpIf') set myCpIf(condition: boolean) { 
} 
Step-3: To make directive globally available in our application, we need to configure it in application module in the same way as we configure component. Find the sample example for configuration.
import { CpIfDirective }  from './directives/cp-if.directive.ts';

@NgModule({
----------------
----------------
  declarations: [
        ---------
        ---------
	CpIfDirective
        ---------
        ---------
  ]
----------------
----------------
})
export class AppModule { } 
Step-4: Now we are ready to use our custom structural directive in HTML template. We can use our directive in the same way as we use Angular in-built structural directive such as 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> 
b. Using directive with ng-template.
<ng-template [cpIf]="showCpIf">
    <div>
	<b>Hello cpIf Directive.</b>
    </div>
</ng-template> 

TemplateRef and ViewContainerRef

To change DOM layout we should use TemplateRef 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) { } 
To add host element in DOM layout, we need to call createEmbeddedView() method of ViewContainerRef. Find the line of code.
this.viewContainer.createEmbeddedView(this.templateRef); 
If we want to clear view container, call 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 like NgIf 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();
	  } 
	}
} 
Here the directive name is 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);
		}
	}
} 
Here directive name is 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);
	}
} 
Here directive name is 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.ts
import { Component } from '@angular/core';

@Component({
   selector: 'app-root',
   templateUrl: './app.component.html'
})
export class AppComponent { 
   showCpIf = false;
   showCpDelay = false;
   delayInSec = 0;
} 
app.component.html
<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> 
app.module.ts
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 { } 
Find the print-screen of the output.
Angular Custom Structural Directive Example

Reference

Structural directives

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us