Create Custom Directive In Angular

Directive In Angular

 
In this article, we will understand what directive is in Angular and why we need directive, and how to create custom directives.

So firstly I will try to explain what directive is in Angular:

Angular Directive is a TypeScript class which is declared as a @directive decorator.
 
The directives allow you to attach behavior to DOM elements and the @directive decorator provides you with an additional metadata that determines how directives should be processed, instantiated, and used at run-time.

Angular Directive is basically a class with a @Directive decorator. You might be wondering, what are decorators? Decorators are functions that modify JavaScript classes. Decorators are used for attaching metadata to classes, it knows the configuration of those classes and how they should work.
 

Three Types of Directives Available in Angular


Component Directives

These form the main class with details of how the component should be processed, instantiated and used at runtime.

Attribute Directives

Attribute directives deal with changing the look and behavior of the dom element.

Structural Directives

A structure directive basically deals with manipulating the dom elements. Structural directives have a * sign before the directive. For example, *ngIf and *ngFor.
 

Why We Need Directives in Angular


If you want similar functionality across all components, for example, fade in – fade out or you need some input type which provide auto-suggestive Google places (I'll explain later in detail), you can take two approaches. You can write code in all components or, in programming language you can create a function and that function will provide the same functionality across all components where you will use this function.
 
Similarly, in Directive, you can write a directive and add behavior inside it. Then whenever you need that behaviour, you can import the directive.
 

Custom Directive

 
For creating Custom Directive in Angular, I have to take an example of auto-suggestive Google places by input control.
 
Before we begin…

To use Google places autocomplete API, we need an API key. To obtain an API key, please go to the Google maps platform and create a new project.
After that, install the package with the following command,
 
npm i --save-dev @types/googlemaps
 
In your index page copy that script in your head section with your secret key:

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR SECRET KEY&libraries=places" async defer></script>

Create a directory in your app root folder with the following command,
 
ng g d google-places.directive

Now edit your google-places.directive.ts.
  1. /// <referencetypes="@types/googlemaps"/>  
  2. import {  
  3.     Directive,  
  4.     ElementRef,  
  5.     OnInit,  
  6.     Output,  
  7.     EventEmitter  
  8. } from '@angular/core';  
  9. @Directive({  
  10.     // tslint:disable-next-line:directive-selector  
  11.     selector: '[google-place]'  
  12. })  
  13. exportclassGooglePlacesDirectiveimplementsOnInit {  
  14.     // tslint:disable-next-line:no-output-on-prefix  
  15.     @Output() onSelect: EventEmitter < any > = newEventEmitter();  
  16.     privateelement: HTMLInputElement;  
  17.     constructor(privateelRef: ElementRef) {  
  18.         // elRef will get a reference to the element where  
  19.         // the directive is placed  
  20.         this.element = elRef.nativeElement;  
  21.     }  
  22.     ngOnInit() {  
  23.         constautocomplete = newgoogle.maps.places.Autocomplete(this.element);  
  24.         // Event listener to monitor place changes in the input  
  25.         google.maps.event.addListener(autocomplete, 'place_changed', () => {  
  26.             // Emit the new address object for the updated place  
  27.             this.onSelect.emit(this.getFormattedAddress(autocomplete.getPlace()));  
  28.         });  
  29.     }  
  30.     /// <Summary>  
  31.     /// return formated address from google places  
  32.     getFormattedAddress(place) {  
  33.         // @params: place - Google Autocomplete place object  
  34.         // @returns: location_obj - An address object in human readable format  
  35.         constlocation_obj = {};  
  36.         // tslint:disable-next-line:forin  
  37.         for (const_valueinplace.address_components) {  
  38.             constitem = place.address_components[_value];  
  39.             location_obj['formatted_address'] = place.formatted_address;  
  40.             if (item['types'].indexOf('locality') > -1) {  
  41.                 location_obj['locality'] = item['long_name'];  
  42.             }  
  43.             elseif(item['types'].indexOf('administrative_area_level_1') > -1) {  
  44.                 location_obj['admin_area_l1'] = item['short_name'];  
  45.             }  
  46.             elseif(item['types'].indexOf('street_number') > -1) {  
  47.                 location_obj['street_number'] = item['short_name'];  
  48.             }  
  49.             elseif(item['types'].indexOf('route') > -1) {  
  50.                 location_obj['route'] = item['long_name'];  
  51.             }  
  52.             elseif(item['types'].indexOf('country') > -1) {  
  53.                 location_obj['country'] = item['long_name'];  
  54.             }  
  55.             elseif(item['types'].indexOf('postal_code') > -1) {  
  56.                 location_obj['postal_code'] = item['short_name'];  
  57.             }  
  58.         }  
  59.         returnlocation_obj;  
  60.     }  
  61. }   
Code Explanation

We have done things right here.
 
We created a characteristic getFormattedAddress which takes a Google place object as an argument and parses it to create a useful deal with an item.
 
We introduced an event listener to the autocomplete API, so as to fire each time the places are changed, and we use this event listener to emit our own event to be able to parse the place object. 
 
We have uncovered this event via an onSelect event emitter. 

In your app.component.html:
  1. <divstyle="text-align:center">  
  2.     <h1> Welcome to {{ title }}! </h1>  
  3.     </div>  
  4.     <divclass="input-container">  
  5.         <input type="text" class="google-place-input" google-place (onSelect)="setAddress($event)" placeholder="Type to search..">  
  6.         <!-- Displaying the contents of the address object received from our event emitter -->  
  7.         <ulstyle="list-style-type: none;">  
  8.             <li*ngFor="let key of addrKeys">  
  9.                 <spanstyle="font-weight: bold">{{key}}</span>: <span>{{addr[key]}}</span>  
  10.                     </li>  
  11.                     </ul>  
  12.                     </div>  
In your app.component.ts,
  1. import {  
  2.     Component,  
  3.     NgZone  
  4. } from '@angular/core';  
  5. @Component({  
  6.     selector: 'app-google-place-search',  
  7.     templateUrl: './google-place-search.component.html',  
  8.     styleUrls: ['./google-place-search.component.css']  
  9. })  
  10. exportclassAppComponent {  
  11.     publictitle = 'Places';  
  12.     publicaddrKeys: string[];  
  13.     publicaddr: object;  
  14.     // Method to be invoked everytime we receive a new instance  
  15.     // of the address object from the onSelect event emitter.  
  16.     setAddress(addrObj) {  
  17.         // We are wrapping this in a NgZone to reflect the changes  
  18.         // to the object in the DOM.  
  19.         this.zone.run(() => {  
  20.             this.addr = addrObj;  
  21.             this.addrKeys = Object.keys(addrObj);  
  22.         });  
  23.     }  
  24.     constructor(privatezone: NgZone) {}  
  25. }   
Add in .css file of your appcomponent,
  1. .input - container {  
  2.     text - align: center;  
  3.     width: 100 % ;  
  4. }.google - place - input {  
  5.     width: 50 % ;  
  6.     min - width: 300 px;  
  7.     height: 1.5 rem;  
  8.     font - size: 1.5 rem;  
  9.     padding: 0.5 rem;  
  10. }   
Now run ng serve, and you will get the output,
 
output