How to create custom Attribute Directive in Angular

Dhananjay Kumar / Tuesday, November 1, 2016

Attribute Directives are used to change the behavior, appearance or look of an element on a user input or via data from the service. Essentially, there are three types of directives in Angular 2:

  1. Component
  2. Structural directives
  3. Attribute directives

In this post, we will learn how to create Attribute Directives in Angular. So let’s say we want to change the background color of an element; in that case we would apply the attribute directive to the element.

Create first Attribute directive

Let’s start with creating the Attribute Directive. To do this, we need to create a class and decorate it with @directive decorators. A simple attribute directive to change the color of an element can be created as shown in the next listing:

import { Directive, ElementRef, Renderer } from '@angular/core';
 
@Directive({
    selector: '[chcolor]'
})
export class ChangeColorDirective {
 
    constructor(private el: ElementRef, private render: Renderer) {
        this.changecolor('red');
    }
 
    private changecolor(color: string) {
 
        this.render.setElementStyle(this.el.nativeElement, 'color', color);
    }
}

While we’re creating an attribute directive to change the color of the element, keep in mind that we don’t need to create a new attribute directive to just change the color; simple colors can be changed by using property binding. However, the attribute directive we created will change color of an element in this example. There are few important points to remember:

  1. Import required modules like Directive, ElementRef, and Renderer from Angular  core library
  2. Create a TypeScript class
  3. Decorate the class with @directive
  4. Set the value of the selector property in @directive decorator function. The directive would be used, using the selector value on the elements.
  5. In the constructor of the class, inject ElementRef and Renderer object.

We are injecting ElementRef in the directive’s constructor to access the DOM element. We are also injecting Renderer in the directive’s constructor to work with DOM’s element style.  We are calling the renderer’s setElementStyle function. In the function, we pass the current DOM element by using the object of ElementRef and setting the color style property of the current element. We can use this attribute directive by its selector in AppComponent as shown in the next listing:

@Component(
    {
        selector: 'app-container',
        template: `<p chcolor>{{message}}</p>`
    })
export class AppComponent {

We used an attribute directive on a

element. It will change the color of the paragraph text to red.  Also, we need to declare the attribute directive at the app.module.ts as shown in the next listing:

  1. We need to import the directive
  2. We need to declare the directive

app.module.ts

import { ChangeColorDirective } from './task/taskcolor.directive';
 
import { app_routing } from './app.routing';
import { DataService } from './shared/data.service';
 
@NgModule({
    imports: [BrowserModule, FormsModule, HttpModule, app_routing],
    declarations: [AppComponent, TasksComponent, HomeComponent, AddtaskComponent, ChangeColorDirective],
    providers: [DataService],
    bootstrap: [AppComponent]
})
export class AppModule { }

Here we are importing the ChageColorDirective and also declaring it in the app.module. After doing this, we should be able to use the attribute directive in all the components.

 User Input in attribute directive

We may have a requirement to apply an attribute directive on the basis of some user inputs to change the color of the element when the user mouses over or mouse hovers on the element. To do this, we need to:

  1. capture user input or action, and
  2. apply a color or clear color respectively.

On various user actions, we can call different methods to handle the user actions. To enable methods to handle user actions such as mouse enter, we need to decorate methods with the @HostListener decorator.  We can modify directives to handle user input as shown in the next listing.

import { Directive, ElementRef, Renderer, HostListener, Input } from '@angular/core';
 
@Directive({
    selector: '[chcolor]'
})
export class ChangeColorDirective {
    constructor(private el: ElementRef, private render: Renderer) { }
 
    @HostListener('mouseenter') methodToHandleMouseEnterAction() {
        this.changecolor('red');
    }
    @HostListener('mouseleave') methodToHandleMouseExitAction() {
        this.changecolor('blue');
    }
    private changecolor(color: string) {
 
        this.render.setElementStyle(this.el.nativeElement, 'color', color);
    }
}

As you can probably tell, we have added two methods to handle user actions. On mouse enter and mouse leave, we are changing the color to red and blue respectively.

Passing data to the directive with binding

So far we have hard coded the value of the color to the directive. In this next section, let us pass data to the directive with binding. To bind the directive with data, we will use the @Input() decorator and add a property in the directive class:

@Input('chcolor') highlightColor: string;

The attribute directive can be used as follows:

<p [chcolor]="color">{{message}}</p>

Let us rewrite the attribute directive to get data from the binding. The element to which the attribute directive will be bound will pass color data.  If the element is not binding color data to the attribute binding, the default color red would be used.

This directive can be recreated as shown in the next listing:

import { Directive, ElementRef, Renderer, HostListener, Input } from '@angular/core';
 
@Directive({
    selector: '[chcolor]'
})
export class ChangeColorDirective {
    private _defaulColor = 'red';
    @Input('chcolor') highlightColor: string;
 
    constructor(private el: ElementRef, private render: Renderer) { }
 
    @HostListener('mouseenter') methodToHandleMouseEnterAction() {
 
        console.log(this.highlightColor);
        this.changecolor(this.highlightColor || this._defaulColor);
    }
    @HostListener('mouseleave') methodToHandleMouseExitAction() {
        console.log(this.highlightColor);
        this.changecolor(null);
    }
 
    private changecolor(color: string) {
        this.render.setElementStyle(this.el.nativeElement, 'color', color);
    }
}
 

We can pass data through binding to the attribute directive as shown in the next listing:

   @Component({
 selector: 'app-container',
 template: `
 <div>
     <input type="radio" name="colors" (click)="color='blue'">blue
     <input type="radio" name="colors" (click)="color='orange'">orange
     <input type="radio" name="colors" (click)="color='green'">green
 </div>
 <p [chcolor]="color">{{message}}</p>`
 })
 export class AppComponent {

We are creating a radio button and binding the value of the color on the click event of the button to the attribute directive. We can select a color from the radio button, and on mouse enter the color would be changed to the selected value.

In a real application, we may fetch data from a REST service and bind to attribute directive. 

Conclusion

In this post we learned about Attribute Directives in Angular , and created them to change the behavior or appearance of an element, and bound it to the data of the element. I hope you find this post useful. Thanks for reading!