Angular Custom Directives Example
January 05, 2021
This page will walk through Angular 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 { }
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.
Contents
- Technologies Used
- Project Structure
- Custom Attribute Directives
- 1. Creating Simple Attribute Directive
- 2. Create Attribute Directive using @Input()
- 3. Create Attribute Directive using @HostListener()
- Custom Structural Directives
- 1. Structural Directive for IF Condition
- 2. Structural Directive for LOOP
- 3. Structural Directive with setTimeout()
- Application Component and Module
- Run Application
- Download Source Code
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 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 { }
Custom directive selector name:
cpDefaultTheme
Example to use our custom directive:
<p cpDefaultTheme> cpDefaultTheme Directive Demo</p>
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; } }
@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>
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; } }
cpCustomTheme
Example to use our custom directive:
<div cpCustomTheme tcolor="blue" tsize="30px"> cpCustomTheme Directive Demo with Custom Settings</div>
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; } }
defColOnEvent
Example to use our custom directive:
<p defColOnEvent> defColOnEvent Directive Demo</p>
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; } }
dynamicColOnEvent
Example to use our custom directive:
<p [dynamicColOnEvent]="myColor" defaultValue="blue"> dynamicColOnEvent Directive Demo</p>
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 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.
References
Attribute Directives
Structural Directives
Angular Custom Attribute Directive Example
Angular Custom Structural Directive Example
Download Source Code
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(); } } }
<div *cpIf="showCpIf"> <b>Hello World!</b> </div> <ng-template [cpIf]="!showCpIf"> <div> <b>Not Available</b> </div> </ng-template>
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); } } }
<ul> <li *cpLoop="3" > Namaste </li> </ul>
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); } }
<div *cpDelay="delayInSec"> <b> Hello World! </b> </div>
<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>
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; } }
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 { }
