Home  >  Angular 2

Angular 2 Custom Directives Example

By Arvind Rai, June 03, 2017
This page will walk through Angular 2 custom directives example. Angular provides three types of directive: component directive, attribute directive and structural directive. Component directive is used to create HTML template. Attribute directive changes the appearance and behavior of DOM element. Structural directive changes the DOM layout by adding and removing DOM elements. On this page we will provide how to create custom attribute and structural directives. In angular we create these directives using @Directive() decorator. It has a selector metadata that defines the custom directive name. Angular also provides built-in directives. The built-in attribute directives are NgStyle, NgClass etc. These directives change the appearance and behavior of HTML elements. The built-in structural directives are NgFor and NgIf etc. These directives can add and remove HTML elements from the DOM layout. Custom directives are created using following syntax.
@Directive({ 
     selector: '[cpDir]' 
})
export class CPDirective {
} 
The directive name is cpDir here. It should be enclosed within bracket []. We can keep directive name as we want but it should be started with your company name or any other keyword but not with Angular keyword such as ng. To behave our directive like attribute directive, we can use ElementRef to change appearance. To listen event we can use @HostListener() decorator. To behave our directive like structural directive, we can use TemplateRef and ViewContainerRef. Now find the complete custom attribute directive and custom structural directive example step by step.

Technologies Used

Find the technologies being used in our example.
1. Angular 4.0.0
2. TypeScript 2.2.0
3. Node.js 6.10.1
4. Angular CLI 1.0.4
5. Angular Compiler CLI 4.0.0

Project Structure

Find the project structure of our example.
angular-demo
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--attribute-directives
|   |   |    | 
|   |   |    |--cp-color.directive.ts
|   |   |    |--cp-custom-theme.directive.ts
|   |   |    |--cp-default-theme.directive.ts
|   |   |    |--default-color-on-event.directive.ts
|   |   |    |--dynamic-color-on-event.directive.ts
|   |   |
|   |   |--structural-directives
|   |   |    | 
|   |   |    |--cp-delay.directive.ts
|   |   |    |--cp-if.directive.ts
|   |   |    |--cp-loop.directive.ts
|   |   |
|   |   |--app.component.html
|   |   |--app.component.ts
|   |   |--app.module.ts 
|   | 
|   |--main.ts
|   |--index.html
|   |--styles.css
|
|--node_modules
|--package.json 

Custom Attribute Directives

Angular custom attribute is created to change appearance and behavior of HTML element. Find the steps to create custom attribute directive.
1. Create a class decorated with @Directive().
2. Assign the attribute directive name using selector metadata of @Directive() decorator enclosed with bracket [] .
3. Use ElementRef class to access DOM to change host element appearance.
4. Use @Input() decorator to accept user input in our custom directive.
5. Use @HostListener() decorator to listen events in custom attribute directive.
6. Configure custom attribute directive class in application module in the declarations metadata of @NgModule decorator.

To change appearance of HTML element in DOM, we need to use ElementRef within custom directive definition. ElementRef can directly access the DOM. We can use it directly without using directive but that can lead to XSS attacks. It is safer to use ElementRef within directive definition. Now let us create custom attribute directives.

1. Creating Simple Attribute Directive

We are creating a simple attribute directive that will change color and font size of a HTML element.
cp-default-theme.directive.ts
import { Directive, ElementRef, AfterViewInit } from '@angular/core';

@Directive({ 
     selector: '[cpDefaultTheme]' 
})
export class CPDefaultThemeDirective implements AfterViewInit {
    constructor(private elRef: ElementRef) {
    }
    ngAfterViewInit(): void {
       this.elRef.nativeElement.style.color = 'blue';
       this.elRef.nativeElement.style.fontSize = '20px';
    }		
} 
AfterViewInit is the lifecycle hook that is called after a component view has been fully initialized. To use AfterViewInit, our class will implement it and override its method ngAfterViewInit().
Directives are declared in application module in the same way as we declare component. We need to configure our each and every directive in application module within the declarations block of @NgModule decorator.
import { CPDefaultThemeDirective }  from './attribute-directives/cp-default-theme.directive';
@NgModule({
  ---
  declarations: [
        ---
	CPDefaultThemeDirective
  ]
})
export class AppModule { } 
Now we are ready to use our custom directive in our application in any component template.
Custom directive selector name: cpDefaultTheme
Example to use our custom directive:
<p cpDefaultTheme> cpDefaultTheme Directive Demo</p> 
The text of the <p> element will be blue with font size 20px.

2. Create Attribute Directive using @Input()

Angular custom directive can also accept input from the user. To accept input within directive we need to declare a property decorated with @Input(). We must use property name same as selector name. If we want to use different property name from selector name then use alias with @Input(). Now alias name will be same as selector name.
We will create a custom directive that will accept input from the user. Here for the example we will accept color as a user input.
cp-color.directive.ts
import { Directive, ElementRef, Input, AfterViewInit } from '@angular/core';

@Directive({ 
     selector: '[cpColor]' 
})
export class CPColorDirective implements AfterViewInit {
    @Input() cpColor: string;
    constructor(private elRef: ElementRef) { 
    }
    ngAfterViewInit(): void {
	this.elRef.nativeElement.style.color = this.cpColor;
    }
} 
Look into the above code, selector name and property name are the same. If we want to use alias we can use as follows.
@Input('cpColor') myColor: string; 

Custom directive selector name: cpColor
Example to use our custom directive:
<h4 [cpColor]="titleColor"> cpColor Directive Demo using Bracket []</h4>
<h4 bind-cpColor="titleColor"> cpColor Directive Demo using bind- prefix  </h4>
<h4 cpColor="{{titleColor}}"> cpColor Directive Demo using Interpolation</h4> 
The text of <h4> will be shown in the user provided color.

If we want to accept more than one input then create more than one properties decorated with @Input().
cp-custom-theme.directive.ts
import { Directive, ElementRef, Input, AfterViewInit } from '@angular/core';

@Directive({ 
     selector: '[cpCustomTheme]' 
})
export class CPCustomThemeDirective implements AfterViewInit {
    @Input() tcolor: string;
    @Input() tsize: string;	
    constructor(private elRef: ElementRef) {
    }
    ngAfterViewInit(): void {
	this.tcolor = this.tcolor || 'green';
	this.tsize = this.tsize || '20px';
        this.elRef.nativeElement.style.color = this.tcolor;
	this.elRef.nativeElement.style.fontSize = this.tsize;
   }	
} 
Custom directive selector name: cpCustomTheme
Example to use our custom directive:
<div cpCustomTheme tcolor="blue" tsize="30px"> cpCustomTheme Directive Demo with Custom Settings</div> 
User is providing color and font size as an input. The text of <div> tag will be shown with the given color and font size.

3. Create Attribute Directive using @HostListener()

If we want to change element appearance in DOM on any event then we need to listen event in our custom directive. To listen event we will use Angular @HostListener() decorator in our custom directive. The event name will be assigned to @HostListener() decorator.
Here we are creating a custom attribute directive using @HostListener() decorator.
default-color-on-event.directive.ts
import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({ 
     selector: '[defColOnEvent]' 
})
export class DefaultColorOnEventDirective {
   constructor(private elRef: ElementRef) { 
   }
   @HostListener('mouseover') onMouseOver() {
     this.changeColor('red');
   }
   @HostListener('mouseleave') onMouseLeave() {
     this.changeColor('black');
   }
   private changeColor(color: string) {
     this.elRef.nativeElement.style.color = color;
   }  
} 
Custom directive selector name: defColOnEvent
Example to use our custom directive:
<p defColOnEvent> defColOnEvent Directive Demo</p> 
The color of text of <p> tag will change on mouse event. On mouse over event , color of text will be red and on mouse leave event, the color of text will be black.

We can also use @HostListener() with @Input() to get user input.
dynamic-color-on-event.directive.ts
import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({ 
     selector: '[dynamicColOnEvent]' 
})
export class DynamicColorOnEventDirective {
   @Input('dynamicColOnEvent') dynamicColor: string;
   @Input() defaultValue: string;
   constructor(private elRef: ElementRef) {
   }
   @HostListener('mouseover') onMouseOver() {
     this.changeBackgroundColor(this.dynamicColor || this.defaultValue);
   }
   @HostListener('mouseleave') onMouseLeave() {
     this.changeBackgroundColor('white');
   }
   private changeBackgroundColor(color: string) {
     this.elRef.nativeElement.style.backgroundColor = color;
   }  
}  
Custom directive selector name: dynamicColOnEvent
Example to use our custom directive:
<p [dynamicColOnEvent]="myColor" defaultValue="blue"> dynamicColOnEvent Directive Demo</p> 
On mouse over, the background color will be user provided color and on mouse leave the background color will be white. If we don't accept the user input color then on mouse over, blue background color as default will be shown.

Custom Structural Directives

Structural directive is used to change the DOM layout by adding and removing DOM elements. Find the steps to create custom structural directive.
1. Create a class decorated with @Directive().
2. Assign the structural directive name using selector metadata of @Directive() decorator enclosed with bracket [] .
3. Create a setter method decorated with @Input(). We need to take care that the method name should be same as directive name.
4. Configure custom structural directive class in application module in the declarations metadata of @NgModule decorator.

To create custom structural directive we need to use TemplateRef and ViewContainerRef etc that will help to change the DOM layout.
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.

Now let us create custom structural directives.

1. Structural Directive for IF Condition

We will create a structural directive that will add a layout in DOM for a true condition otherwise it will delete it from DOM.
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();
	   } 
	}
} 
Custom directive selector name: cpIf
Example to use our custom directive:
<div *cpIf="showCpIf">
  <b>Hello World!</b>
</div>
<ng-template [cpIf]="!showCpIf">
      <div>
	  <b>Not Available</b>
      </div>
</ng-template> 
When showCpIf is true then Hello World! message will be shown otherwise the message will be Not Available .

2. Structural Directive for LOOP

We will create a structural directive that will add an element in DOM layout for the given number of time. It will work as a loop.
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);
		}
	}
} 
Custom directive selector name: cpLoop
Example to use our custom directive:
<ul>
 <li *cpLoop="3" >
     Namaste
 </li> 
</ul> 
The <li> tag will be added 3 times in DOM layout.

3. Structural Directive with setTimeout()

Now we will create a structural directive that will add an element in DOM layout after a given time.
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);
	}
} 
Custom directive selector name: cpDelay
Example to use our custom directive:
<div *cpDelay="delayInSec">
   <b> Hello World! </b>	     
</div> 
Once the time given by delayInSec is over, the message will be displayed.

Application Component and Module

app.component.html
<h2>Custom Attribute Directives</h2>

<p cpDefaultTheme> cpDefaultTheme Directive Demo</p>

<h4 [cpColor]="titleColor"> cpColor Directive Demo</h4>

<div cpCustomTheme> cpCustomTheme Directive Demo with Default Settings</div>

<div cpCustomTheme tcolor="blue" tsize="30px"> cpCustomTheme Directive Demo with Custom Settings</div>

<p defColOnEvent> defColOnEvent Directive Demo</p>

<div>
  <select [(ngModel)] ="myColor">
        <option value='' selected disabled> Select Color </option> 
	<option *ngFor = "let color of colors" [value] = "color">
		{{color}}
	</option>
  </select>
  <p [dynamicColOnEvent]="myColor" defaultValue="blue"> dynamicColOnEvent Directive Demo</p>
</div>

<h2>Custom Structural Directives</h2>

<h3>cpIf Directive</h3>
<div>
  <input type="radio" name="rad1" (click)= "changeCondition(true)"> True
  <input type="radio" name="rad1" (click)= "changeCondition(false)"> False
</div>
<br/>
<div *cpIf="showCpIf">
  <b>Hello World!</b>
</div>
<ng-template [cpIf]="!showCpIf">
   <div>
	  <b>Not Available</b>
   </div>
</ng-template>

<h3>cpLoop Directive</h3>
<ul>
 <li *cpLoop="3" >
     Namaste
 </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 World! </b>	     
    </div>
</div> 
app.component.ts
import { Component } from '@angular/core';

@Component({
   selector: 'app-root',
   templateUrl: './app.component.html'
})
export class AppComponent { 
   txtsize = '25px';
   colors = ['CYAN', 'GREEN', 'RED'];
   myColor = '';
   titleColor='green'
   
   showCpIf = false;
   showCpDelay = false;
   delayInSec = 0;
   
   changeCondition(flag) {
      this.showCpIf = flag;
   }
} 
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 { CPDefaultThemeDirective }  from './attribute-directives/cp-default-theme.directive';
import { CPColorDirective }  from './attribute-directives/cp-color.directive';
import { CPCustomThemeDirective }  from './attribute-directives/cp-custom-theme.directive';
import { DefaultColorOnEventDirective }  from './attribute-directives/default-color-on-event.directive';
import { DynamicColorOnEventDirective }  from './attribute-directives/dynamic-color-on-event.directive';
import { CpIfDirective }  from './structural-directives/cp-if.directive';
import { CpDelayDirective }  from './structural-directives/cp-delay.directive';
import { CpLoopDecorator }  from './structural-directives/cp-loop.directive';

@NgModule({
  imports: [     
        BrowserModule,
	FormsModule
  ],
  declarations: [
        AppComponent,
	CPDefaultThemeDirective,
	CPColorDirective,
	CPCustomThemeDirective,
	DefaultColorOnEventDirective,
	DynamicColorOnEventDirective,
	CpIfDirective,
	CpDelayDirective,
	CpLoopDecorator
  ],
  bootstrap: [
        AppComponent
  ]
})
export class AppModule { } 

Run Application

To run the application, find following steps.
1. Download source code using download link given on this page.
2. In your angular CLI application, replace src folder from the downloaded one.
3. Run ng serve using command prompt.
4. Now access the URL http://localhost:4200 . Find the print screen.
Angular 2 Custom Directives Example


I am done now. Happy Angular 2+ learning!

References

ATTRIBUTE DIRECTIVES
STRUCTURAL DIRECTIVES
Angular 2 Custom Attribute Directive Example
Angular 2 Custom Structural Directive Example

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
FIND MORE TUTORILAS





Copyright ©2017 concretepage.com, all rights reserved |Privacy Policy | Contact Us