Monday, July 5, 2021

How to Create a Custom Attribute Directive in Angular

In this article we’ll see how to create a custom attribute directive in Angular. Attribute directive is used to modify the appearance or behavior of DOM element.

Angular provides a class named ElementRef which is a wrapper class around a native element inside of a View so it can access DOM. Permitting direct access to the DOM can make your application more vulnerable to XSS (Cross-Site Scripting) attacks. It is recommended to create a custom directive and use ElementRef class inside it to change the appearance or behavior of DOM element.

In Angular there is also a Renderer2 class that helps in implementing custom rendering. So in our custom attribute directive we'll make use of the Renderer2 and ElementRef classes to manipulate elements.


Steps to create a custom directive in Angular

1. Create a class decorated with @Directive.

You can use the following command to create a Directive class.

ng generate directive customdirective

It will generate customdirective.directive.ts Typescript file, a test file customdirective.directive.spec.ts and also updates app.module.ts to register the created directive.

Generated Directive class-

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

@Directive({
  selector: '[appCustomdirective]'
})
export class CustomdirectiveDirective {

  constructor() { }

}

You can notice that the selector for the directive is enclosed in square brackets [''] which means that the selector name passed will be a property of an element, not used as an element itself.

Custom attribute directive in Angular example

In this example we’ll create a custom directive that will set the background color for the element where it is used. Later we’ll make changes in the directive to make it react to the user events, pass value to it.

Custom directive with changes to set the background color style for the host element.

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

@Directive({
  selector: '[appCustomdirective]'
})
export class CustomdirectiveDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) { 
    this.highLight('yellow');
  }

  private highLight(color : string){
    this.renderer.setStyle(this.el.nativeElement,'backgroundColor',color);
  }
}

In the constructor instance of ElementRef and Renderer2 are injected using dependency injection.

There is a highLight() method where setStyle() method of the Renderer2 is called to set the backgroundColor style of the host element. Host element is the element where this directive will be placed.

Applying an attribute directive

Let's add the selector of the directive in an element to see our custom directive in action.

Add the following in app.component.html. Here <span> is the host element which is using directive as an attribute.

<p>This content is highlighted using a <span appCustomdirective>custom directive</span></p>

Now if we compile and run our app-

custome attribute directive Angular

Handling user events in custom directive

Now we have a directive that sets the background style of the host element. Let’s extend this functionality to set the style based on a user event.

There is a @HostListener decorator in Angular that declares a DOM event to listen for, and provides a handler method to run when that event occurs.

Using @HostListener decorator we’ll listen to 'mouseenter' and 'mouseleave' events in our custom directive and also provide handler methods to set the background style when these events occur.

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

@Directive({
  selector: '[appCustomdirective]'
})
export class CustomdirectiveDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) { 
    //this.highLight('yellow');
  }

  @HostListener('mouseenter') onMouseEnter() {
    this.highLight('yellow');
  }
  
  @HostListener('mouseleave') onMouseLeave() {
    this.highLight('');
  }
  
  private highLight(color : string){
    this.renderer.setStyle(this.el.nativeElement,'backgroundColor',color);
  }
}

As you can see there are two handler methods-

  • onMouseEnter()- To listen to 'mouseenter' event.
  • onMouseLeave()- To listen to 'mouseleave' event.

With that change you will see that “custom directive” text get highlighted only when you bring mouse over it.

Setting host element property in custom directive

In Angular there is a @HostBinding() Decorator that binds the host property. Angular automatically checks host property bindings during change detection, and if a binding changes it updates the host element of the directive.

Using @HostBinding decorator we’ll bind to the properties of the host element from the directive class and manipulate the host element using that bound property.

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

@Directive({
  selector: '[appCustomdirective]'
})
export class CustomdirectiveDirective {
  constructor(private el: ElementRef, private renderer: Renderer2 { }

  @HostBinding('style.border') border: string;

  @HostListener('mouseenter') onMouseEnter() {
    this.highLight('yellow');
    this.border = '2px dashed green';
  }
  
  @HostListener('mouseleave') onMouseLeave() {
    this.highLight('');
    this.border = '';
  }

  private highLight(color : string){
    this.renderer.setStyle(this.el.nativeElement,'backgroundColor',color);
  }
}

In the custom directive using @HostBinding decorator style.border property of the host element is bound to the border property of the Directive class. Now, if a bound property changes that change is also reflected in the host element of the directive.

When this assignment this.border = '2px dashed green'; is done style.property of the host element is also updated.

@hostlistener with custom directive

Passing values into an attribute directive

You can also pass values to the directive using the @Input decorator. Using this feature let’s change the custom directive to pass the background style color to the directive rather than having a fixed value in the directive.

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

@Directive({
  selector: '[appCustomdirective]'
})
export class CustomdirectiveDirective {
  @Input('appCustomdirective') highlightColor: string;

  constructor(private el: ElementRef, private renderer: Renderer2) { }

  @HostBinding('style.border') border: string;

  @HostListener('mouseenter') onMouseEnter() {
    this.highLight(this.highlightColor);
    this.border = '2px dashed green';
  }
  
  @HostListener('mouseleave') onMouseLeave() {
    this.highLight('');
    this.border = '';
  }

  private highLight(color : string){
    this.renderer.setStyle(this.el.nativeElement,'backgroundColor',color);
  }
}

A property “highlightColor” is added in the custom directive decorated with @Input decorator. It also uses the alias with the same name as the directive’s selector so that the same attribute can be used to apply directive and pass data.

Change in app.component.html to pass the color value.

<p>This content is highlighted using a <span [appCustomdirective]="'orange'">custom directive</span></p>
custom directive with @Input

That's all for this topic How to Create a Custom Attribute Directive in Angular. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Angular Tutorial Page


Related Topics

  1. How to Create a Custom Structural Directive in Angular
  2. Directives in Angular
  3. Angular ngStyle Directive With Examples
  4. Angular ngClass Directive With Examples
  5. How to Use ngFor and ngIf on Same Element in Angular

You may also like-

  1. Angular Route Parameters - Setting and Fetching
  2. Custom Pipe in Angular With Example
  3. Angular - Call One Service From Another
  4. Angular Cross Component Communication Using Subject Observable
  5. How HashMap Works Internally in Java
  6. Tree Sort in Java Using Binary Search Tree
  7. Spring Boot REST API CRUD Example With Spring Data JPA
  8. Python Program to Find Factorial of a Number

No comments:

Post a Comment