Angular Custom Directives - A Practical Guide For Angular Developers

angular-custom-directive-with-example-code

In this article, I am going to dive deep into what angular directives are and how you can build your own.

Overview

Angular Directives are of three kinds:
Components, Structural and Attribute Directives e.g NgIf. Component directives are directive with a template while structural as the name implies are used to change the structure of the DOM by adding and removing elements from the DOM.
Attribute directives is used to extend the behavior of component, an element or even another directives.

Build a simple Attribute directive

Building a directive in Angular is as simple as decorating a class with Directive and add metadata like so:

import {Directive} from '@angular/core';

@Directive({
  selector: '[greetings-directive]',
})
export class GreetingsDirective {
...
}

The next thing is to tell angular that we have a directive named GreetingsDirective by adding it to the declarations array in the AppModule like so:
declarations: [
 AppComponent,
 GreetingsDirective
],

Now our GreetingsDirective is ready to be used in our application. Note that a directive must belong to one and only one NgModule.

Using the directive is as simple as adding the selector to an element as attribute like so:

Greeting Directive

In the above code, the h3 tag is called the host element of the directive.

Interacting with the Host

Let's say we want to change the innerHTML of the host element, in this case the h3 tag, we can do that by importing the ElementRef symbol from the Angular core library, and the inject it to the directive's constructor like so:
import { Directive, ElementRef } from "@angular/core";

@Directive({
  selector: "[greetings-irective]"
})
export class GreetingsDirective {
  constructor(elem: ElementRef) {
    elem.nativeElement.innerHTML = "Hello Directive";    
  }
}
As you can see, the ElementRef gave the directive a direct and full access to the host element - that is the element upon which the directive is attached. This will change the text of the the h3 tag to Hello Directive.

Binding host element event with directive

In order to bind the directive to a host’s click event, we’re going to use the decorator HostListener.
The hostlistener allows a directive to listen to events on its host element. We’ll do this by decorating a function on the component with the @HostListener() decoration.

In this example let's listen to the click event on the host element, we will do this by adding @HostListener('click') on the function method we want to call when the host is clicked like so:
import { Directive, ElementRef, HostListener } from "@angular/core";

@Directive({
  selector: "[greetings-irective]"
})
export class GreetingsDirective {
  constructor(elem: ElementRef) {
    elem.nativeElement.innerHTML = "Hello Directive";
  }

  @HostListener("click")   showGreetingAlert(): void {
    alert("Hello Directive");
  }
}



Now when the host is clicked, the showGeetingAlert function will be called and we will get an alert saying Hello Directive.

Creating a Simple Directive to Convert Text to Title Case

Let's build an attribute directive that will change it host element value text to title case. After building the directive, we can then simply add it to an input element like so:

Setup the Directive using Angular CLI

We are going to create a new directive using angular cli like so
ng generate directive directive/titleCase

The above command will generate our directive within a folder named directive within the app folder and also include the directive in the declaration section in the app module.

Our src/app directory will now look like this:

src/app/directive

Bringing our directive to live

Open up title title-case.directive.spec.ts

The first thing we need to do is to change the selector from appTitleCase to app-title-case like so
@Directive({
  selector: '[app-title-case]'
})

The second step is to inject ElementRef which will give the directive full access to the host element. Our directive should like so after ElementRef injection
import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[app-title-case]'
})
export class TitleCaseDirective {

  elemRef :ElementRef;
  constructor(private el:ElementRef) { 
    this.elemRef =el;
  }

}

Next we need a way to listen to the host element event by bringing in HostListener like so:
@HostListener('focusout') onfocusOut(){ }



As you can see we want to listen to the focusout event of the host element and then do the conversion. We will use regular expression to convert the text to title case like so:
toTitleCase(input){
    return input.replace(/\w\S*/g, (txt => txt[0].toUpperCase() + txt.substr(1).toLowerCase() ));
  }

The Directive Complete Code

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[app-title-case]'
})
export class TitleCaseDirective {

  elemRef :ElementRef;
  constructor(private el:ElementRef) { 
    this.elemRef =el;
  }

  @HostListener('focusout') onfocusOut(){    
    this.elemRef.nativeElement.value= this. toTitleCase(this.elemRef.nativeElement.value);
  }

  toTitleCase(input){
    return input.replace(/\w\S*/g, (txt => txt[0].toUpperCase() + txt.substr(1).toLowerCase() ));
  }

}

We listen to to the focusout of the host element and the access the value using ElementRef, convert it to title case and then update the value with the result. Our directive is now ready to be used in our application. Happy Coding!

No comments

Powered by Blogger.