Lukas Marx
May 11, 2019
Angular

Angular Custom Directives

In this tutorial, you will learn what angular attribute directives are and how to create custom directive.

We will discover how to use directives to alter the appearance of DOM-element, react to user input and how to pass input to directives.

In the process it will also become apparent when to use a angular directive vs a component.

Let's get started!

what-are-custom-directives

What is an angular attribute directive?

An angular attribute directive can be simply described as a component without a template. Instead, it is directly using the element it is applied to.

Attribute directive also called custom directives are used when no additional template is needed. The directive can execute logic and apply visual changes to the element it is applied to. This is useful if you want to alter the behavior or style of existing HTML-elements, without wrapping them into a new component.

build custom directive

Creating a custom angular directive with the directive decorator

Creating a custom directive is easy. Just create a new class and decorate it with the @Directive decorator.

It's even easier with the angular-cli:

ng generate directive example

We need to make sure that the directive is declared in the corresponding (app-) module before we can use it. If you are using the angular-cli this should be done automatically.

The generated directive looks like this:

import { Directive } from '@angular/core'

@Directive({
  selector: '[appExample]',
})
export class ExampleDirective {
  constructor() {}
}

After all just a class with a decorator...

using-the-directive-selector

Using the directive selector

Every directive has a selector, just like every component does. This selector is used to apply the directive to elements in your templates. You can use whatever selector you like, but it is best practice to prefix your directives with the prefix of your application.

In case you did not change that manually the prefix is "app". This is done to prevent duplicate selectors when using third-party libraries.

The directive can be applied to HTML-elements in your templates using its selector. In this example we are using it with a button:

<button appExample>Button</button>

This button is now controlled by our example directive.

directive-appearance

Altering the appearance of the element

Having an empty directive is kind of pointless.

So let's change the appearance of the element (the button) from within the directive.

To do that, we can request a reference to the native DOM-element in the constructor of the directive via dependency injection.

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

@Directive({
  selector: '[appExample]',
})
export class ExampleDirective {
  constructor(elementRef: ElementRef) {}
}

Using this element reference, we can alter the element directly. For example, we can change its styles:

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

@Directive({
  selector: '[appExample]',
})
export class ExampleDirective {
  constructor(elementRef: ElementRef) {
    elementRef.nativeElement.style.backgroundColor = '#fff'
  }
}

Angular Universal compatibility

If you want your directive to be compatible with server-side rendering and Angular Universal, we need to take a different approach.

Because there is no full DOM-implementation on node.js servers, we can't access the element directly as we did above.

Instead, we need to use an abstraction-layer on the DOM which is utilized by the so-called Renderer2.

Using this renderer, we can make changes to the appearance like so:

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

@Directive({
  selector: '[appExample]',
})
export class ExampleDirective {
  constructor(elementRef: ElementRef, renderer: Renderer2) {
    renderer.setStyle(elementRef.nativeElement, 'backgroundColor', '#fff')
  }
}

user-events

Reacting to user events

To make our element interactive, we can adjust our directive to react to user events like mouse inputs and update the appearance accordingly.

For example, we can alter the elements backgroundColor when the mouse hovers over the element. Please be aware that this is only one possible application for this and is meant only for demonstration purposes.

This can be done using @HostListener decorators. These are applied to methods which are executed when the corresponding event is fired. HostListeners are not specific to decorators but can be used in components, as well.

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

@Directive({
  selector: '[appExample]',
})
export class ExampleDirective {
  constructor(private elementRef: ElementRef, private renderer: Renderer2) {
    this.setBgColor('white')
  }

  setBgColor(color: string) {
    this.renderer.setStyle(
      this.elementRef.nativeElement,
      'backgroundColor',
      color
    )
  }

  @HostListener('mouseenter') onMouseEnter() {
    this.setBgColor('yellow')
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.setBgColor('white')
  }
}

input-directive

Passing input into the directive using @Input

In case we want to create heavily reuseable directives, it can be handy to be able to pass values into the directive.

This is done by creating a property in the directive that is decorated by the @Input decorator. Again, this is not specific to decorators.

If we wanted to pass a default color to our directive, we could do so by creating a "defaultColor" input:

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

@Directive({
  selector: '[appExample]',
})
export class ExampleDirective implements OnInit {
  @Input() defaultColor: string

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

  ngOnInit() {
    if (this.defaultColor) {
      this.setBgColor(this.defaultColor)
    } else {
      this.setBgColor('white')
    }
  }

  setBgColor(color: string) {
    this.renderer.setStyle(
      this.elementRef.nativeElement,
      'backgroundColor',
      color
    )
  }
}

The input can than be passed from outside using the template syntax:

<button appExample defaultColor="red">Button</button>

Conclusion

In this tutorial we discovered, how we can use custom directives in our angular application.

I hope you liked this article. If you did, please share it with your friends!

Happy Coding!


Leave a comment

We save your email address, your name and your profile picture on our servers when you sing in. Read more in our Privacy Policy.