Custom Directive In Angular
In this article, we will learn how to create a Custom Directive in Angular. The Angular directives allow us to expand or alter the Document Object Model (DOM). The directives can be used to change the appearance, behavior, or layout of a DOM element. We will create four directive examples and show you how to use them.
- Create a custom directive using the @Directive decorator.
- We will create both custom attribute directive & custom Structural directive.
- How to setup selectors
- Pass value to it using the @input.
- How to respond to user inputs,
- Manipulate the DOM element (Change the Appearance) etc.
Angular Directives
There are three sorts of directives in Angular.
- Components
- Structural Directives
- Attribute Directives
Components are Template directives (or views). We understand how to create Angular Components. There is no view linked with the structural and attribute directives.
By adding and removing DOM elements, structural directives alter the DOM layout. The Asterix (*) sign precedes all structural Directives.
The Attribute directives can alter an element's look or behavior.
Creating Custom Attribute Directive
Angular comes with a number of built-in attribute directives. Let's construct a ttClass directive that allows us to give an element a class. Angular ngClass directive is similar.
Create a new directive using the following command:
ng generate directive tt-class.directive.ts
@Input() ttClass: string;
The attribute directive is attached to an element called the parent element. We need to retrieve the reference to alter the properties of the parent element. When we ask for the instance of the ElementRef in its constructor, Angular injects the parent element.
constructor(private el: ElementRef) { }
The Parent DOM element is wrapped with ElementRef. The nativeElement attribute gives us access to the DOM element. We can add a class to an element using the classList method.
ngOnInit() { this.el.nativeElement.classList.add(this.ttClass); }
The full code can be found below:
import { Directive, ElementRef, Input, OnInit } from '@angular/core' @Directive({ selector: '[ttClass]', }) export class ttClassDirective implements OnInit { @Input() ttClass: string; constructor(private el: ElementRef) { } ngOnInit() { this.el.nativeElement.classList.add(this.ttClass); } }
The CSS class blue and the file app.component.css
.blue { background-color: lightblue; }
Finally, add our customer directive ttClass to the button element in the component template.
<button [ttClass]="'blue'">Click Me</button>
Creating Custom Structural Directive
ng generate directive tt-if.directive.ts
Our if condition will be kept in this variable.
_ttif: boolean;
We'll need ViewContainerRef and TemplateRef instances because we'll be modifying the DOM.
constructor(private _viewContainer: ViewContainerRef, private templateRef: TemplateRef<any>) { }
@Input() set ttIf(condition) { this._ttif = condition this._updateView(); }
All of the magic takes place here. If the condition is true, we use the ViewContainerRef's createEmbeddedView function to insert the template. The clean command removes the template from the document object model (DOM).
_updateView() { if (this._ttif) { this._viewContainer.createEmbeddedView(this.templateRef); } else { this._viewContainer.clear(); }
That is all there is to it. Remember to include ttIfDirective in the app.module.ts declaration array. The full code can be found below.
import { Directive, ViewContainerRef, TemplateRef, Input } from '@angular/core'; @Directive({ selector: '[ttIf]' }) export class ttIfDirective { _ttif: boolean; constructor(private _viewContainer: ViewContainerRef, private templateRef: TemplateRef<any>) { } @Input() set ttIf(condition) { this._ttif = condition this._updateView(); } _updateView() { if (this._ttif) { this._viewContainer.createEmbeddedView(this.templateRef); } else { this._viewContainer.clear(); } } }
Open the app.component.ts and add the following code:
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title: string = "Custom Directives in Angular"; show=true; }
Open the app.component.html and add the following code:
<h1> {{title}} </h1> Show Me <input type="checkbox" [(ngModel)]="show"> <div *ttIf="show"> Using the ttIf directive </div> <div *ngIf="show"> Using the ngIf directive </div>
Now, run the application and compare the ngIf & our custom directive ttIf side by side.
Custom Directive Examples
Toggle Directive
@HostListener('click') private onClick() { }
The ttToggleDirective's whole code is as follows.
import { Directive, ElementRef, Renderer2, Input, HostListener, HostBinding } from '@angular/core' @Directive({ selector: '[ttToggle]', }) export class ttToggleDirective { private elementSelected = false; constructor(private el: ElementRef) { } ngOnInit() { } @HostListener('click') private onClick() { this.elementSelected = !this.elementSelected; if (this.elementSelected) { this.el.nativeElement.classList.add('toggle') } else { this.el.nativeElement.classList.remove('toggle') } } }
Open the app.component.css and add the following code:
.toggle { background-color: yellow }
Use it in the following example.
<button ttToggle>Click To Toggle</button>
Tooltip Directive
import { Directive, ElementRef, Renderer2, Input, HostListener } from '@angular/core' @Directive({ selector: '[ttToolTip]', }) export class ttTooltipDirective { @Input() toolTip: string; elToolTip: any; constructor(private elementRef: ElementRef, private renderer: Renderer2) { } @HostListener('mouseenter') onMouseEnter() { if (!this.elToolTip) { this.showHint(); } } @HostListener('mouseleave') onMouseLeave() { if (this.elToolTip) { this.removeHint(); } } ngOnInit() { } removeHint() { this.renderer.removeClass(this.elToolTip, 'tooltip'); this.renderer.removeChild(document.body, this.elToolTip); this.elToolTip = null; } showHint() { this.elToolTip = this.renderer.createElement('span'); const text = this.renderer.createText(this.toolTip); this.renderer.appendChild(this.elToolTip, text); this.renderer.appendChild(document.body, this.elToolTip); this.renderer.addClass(this.elToolTip, 'tooltip'); let hostPos = this.elementRef.nativeElement.getBoundingClientRect(); let tooltipPos= this.elToolTip.getBoundingClientRect(); let top = hostPos.bottom+10 ; let left = hostPos.left; this.renderer.setStyle(this.elToolTip, 'top', `${top}px`); this.renderer.setStyle(this.elToolTip, 'left', `${left}px`); } }
Open the app.component.css and add the following code:
.tooltip { display: inline-block; border-bottom: 1px dotted black; position: absolute; }
Open the app.component.html and add the following code:
<button ttToolTip toolTip="Tip of the day">Show Tip</button>
Now, run the application and see the output screen.