a blog for those who code

Thursday 21 February 2019

Creating Custom Attribute Directives with Angular


In this post we will be discussing about how to create custom Attribute directives with Angular. An attribute directive is one which changes the appearance or the behavior of a DOM element. The steps to create a custom attribute directive is -

1. Create a Directive by annotating a class with the @Directive decorator
2. Register the directive in the app.modeule.ts under declarations
3. Use it in View as an attribute

You can use CLI Command to create a directive

ng generate directive myDirective

This command will create two files my-directive.directive.spec.ts and my-directive.directive.ts. Also it will register the directive in app.module.ts file. The contents of the my-directive.directive.ts file is shown below:

my-directive.directive.ts

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

@Directive({
  selector: '[appMyDirective]'
})
export class MyDirectiveDirective {
  constructor() { }
}

As you can see in the above code, @Directive is the decorator function where we have a selector property (appMyDirective) which is actually our directive name. Thus wherever we use appMyDirective attribute in the template, Angular locates it and applies the logic of my directive to that element.

Now we also have an empty constructor which as of now does nothing. So we can update the constructor like below, which actually changes the element's color to red.

export class MyDirectiveDirective {
  constructor(elRef: ElementRef) {
    elRef.nativeElement.style.color = 'red';
  }
}

Note we have to import ElementRef from Angular core library. ElementRef provides us the reference of the host DOM element by its nativeElement property and thus we can change the color to red.

To use this attribute in the template, we just have to add the above directive as an attribute to the element as whoen below

<p appMyDirective>Using My Directive !!!</p>

So in a nutshell whenever Angular finds appMyDirective it creates an instance of the Custom Directive i.e. MyDirectiveDirective and it passes the element to MyDirectiveDirective constructor which changes the color to red.

A better approach to change the Style would be to use Renderer2, so our class has to be changed as shown below :

export class MyDirectiveDirective implements OnInit {
  constructor(private elRef: ElementRef, private renderer: Renderer2) { }
 
  ngOnInit() {
    this.renderer.setStyle(this.elRef.nativeElement, 'color', 'red', false, false)
  }
}

So we have created our basic Attribute Directive which when used will change the style of the element. Let's say we want to respond to user-initiated events i.e. mouseover, mouseenter, mouseleave and want to change the appearance of the element by responding to those events, this is where HostListener will come into play. HostListener decorator allows us to listen to DOM events like focus, blur, mouseover, mousleave, mousenter etc by subscibing to the rasied events.

export class MyDirectiveDirective {
  constructor(private elRef: ElementRef, private renderer: Renderer2) { }

  @HostListener('mouseenter') mouseenter(eventData: Event) {
    this.renderer.setStyle(this.elRef.nativeElement, 'color', 
                           'red', false, false)
  }

  @HostListener('mouseleave') mouseleave(eventData: Event) {
    this.renderer.setStyle(this.elRef.nativeElement, 'color', 
                           'blue', false, false)
  }
}

In the above code we are subscibing to the mouseenter and mouseleave events of the DOM element using @HostListener decorator.

Instead of using the renderer we can also use @HostBinding decorator as shown below. We have to bind the @HostBinding to a property (string type) using which we will calling other properties. Also we have to pass a string to @HostBinding specifying which property of the Host element we want to bind as shown below :

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

and then we will change our class as shown below :

export class MyDirectiveDirective {
  @HostBinding('style.color') color: string = 'blue';
  constructor(private elRef: ElementRef, private renderer: Renderer2) { }

  @HostListener('mouseenter') mouseenter(eventData: Event) {
    this.color = 'red';
  }
  @HostListener('mouseleave') mouseleave(eventData: Event) {
    this.color = 'blue';
  }
}

Till now we are hardcoding the color, let's pass the data to our Custom Directive to make it more dynamic using Custom Property Binding and @Input. So we will assign two colors first which can be changed using property binding as shown below.

export class MyDirectiveDirective {
  @Input() firstColor: string = 'blue';
  @Input() nextColor: string = 'red';
 
  @HostBinding('style.color') color: string = this.firstColor;
  constructor(private elRef: ElementRef, private renderer: Renderer2) { }

  @HostListener('mouseenter') mouseenter(eventData: Event) {
    this.color = this.firstColor;
  }

  @HostListener('mouseleave') mouseleave(eventData: Event) {
    this.color = this.nextColor;
  }
}

As shown above we have created two input properties firstColor and nextColor and thus we will pass the value along with attribute as shown below

<p appMyDirective [firstColor]="'yellow'" 
            [nextColor]="'black'">Using My Directive !!!</p>

Now if we run our application we will get yellow first and then when hovering we will get black color. Thus we have finished creating our Custom Directive and also passing values thus making it dynamic.

No comments:

Post a Comment