Build Custom Directives in Angular 2

Published Nov 21, 2016Last updated Jan 18, 2017

Directives are the most fundamental unit of Angular applications. As a matter of fact, the most used unit, which is a component, is actually a directive. Components are high-order directives with templates and serve as building blocks of Angular applications.

How to write components in Angular 2 is everywhere on the web so we are not talking about that today. What we will be exploring today is Angular 2's directives; types, when to use them, and how to build one for our custom needs.

Types of Directives

Angular 2 categorizes directives into 3 parts:

Directives with templates known as Components

Directives that creates and destroys DOM elements known as Structural Directives

Directives that manipulate DOM by changing behavior and appearance known as Attribute Directives

Components are what we have been playing with since Angular 2 was introduced so there is no need talking about it. We may go ahead and discuss the attribute and structural directives.

Attribute Directives

Attribute directives, as the name goes, are applied as attributes to elements. They are used to manipulate the DOM in all kinds of different ways except creating or destroying them. I like to call them DOM-friendly directives.

Directives in this categories can help us achieve one of the following tasks:

Apply conditional styles and classes to elements

<p [style.color]="'blue'">Directives are awesome</p>

Hide and show elements, conditionally (different from creating and destroying elements)

<p [hidden]="shouldHide">Directives are awesome</p>

Dynamically changing the behavior of a component based on a changing property

Structural Directives

Structural directives are not DOM-friendly in the sense that they create, destroy, or re-create DOM elements based on certain conditions.

This is a huge difference from what hidden attribute directive does. This is because hidden retains the DOM element but hides it from the user, whereas structural directives like *ngIf destroy the elements.

*ngFor and [ngSwitch] are also common structural directives and you can relate them to the common programming flow tasks.

Custom Attribute Directives

We had a quick look on directives and types of directives in Angular 2. And as you just found out, using the existing directives is very simple. Let's now dig a little deeper and create some to suit our own needs.

Let's get started

Setup a basic Angular app using Angular Quickstart or any other method of your choice. The quickstart already comes with a simple app component so we can just build on that. What we would do now is to create a shared folder in the app directory to hold all our custom directives and then export them using NgModule.

myHidden: Case Study

Our first directive is going to be a case study of the existing Angular 2 hidden directive. Let's implement that and it would serve as an eye opener of how these things work internally:

At this point, the directive is available for us to use anywhere in our app. Let's apply it somewhere in the app component's template:

<!--./app/app.component.html-->
<h1>Welcome</h1>
<!--This will not be shown-->
<h1 myHidden>Hidden Welcome</h1>

We are adding the directive as an attribute (myHidden) to the markup

You can see that one title gets displayed and the other's hidden. Hidden not removed from the DOM, as the console shows. The Angular core hidden directive could take a boolean to hide or not hide based on the value.

This time we are using the Input decorator to receive value form the template and pass it down to the directive. We have to move the implementation from the constructor to ngOnInit lifecycle method because myhidden property will be set late. ngOnInit will wait for all initialization processes to be complete before executing.

The attribute can now be added to our template as an input surrounded with [] and a boolean value passed to it:

In the example above, we chose not to perform any action in the constructor or a lifecycle method. We chose to write a method called hover which is decorated with Host Listeners. The method is called by the host listeners and the host listeners are event listeners attached on the element hosting the directive.

Host Listeners are event listeners attached to any element that hosts (the directive is placed on) the directive.

The most important difference with the way we created our attribute directive is how they are provided to the DOM. Attribute directive uses ElementRef and Renderer to render and re-render while structural directives use TemplateRef and ViewContainerRef to update the DOM content.

The directive has an Input setter that receives a boolean value. If the boolean value resolves to true, we use the ViewContainer's createEmbeddedView method to render the template. We can get hold of the template via the templateRef.

When the the boolean resolves to false, we clear the ViewContainer.

The so-called ViewContainer, in this case, refers to the structural directive host.

You can go ahead to declare and export the directive in the SharedModule:

Our new if directive case study can now be applied anywhere in the app:

<!-- ./app/app.component.ts -->
<div *myIf="false">
Inside if
</div>

Attribute vs Structural: When to use which?

In cases where you need to choose either an attribute directive or a structural directive, deciding between the two might get confusing and you might end up with the wrong choice just because it seems like it solves the problem—but the solution might be limited to a certain extent.

One good example is during a situation about visibility where you are stuck between choosing whether to use the hidden attribute directive or the ngIf structural directive.

This simple rule can guide you when that happens—if the element that hosts the directive will still be useful in the DOM even when it is not visible, then it is a good idea to hide it than to remove it. Otherwise, the element should be removed from the DOM because it is more efficient to keep and manage fewer items on the DOM.

There is always a little price to pay, though. Hiding will keep your DOM intact and you just need toggle around. But this might also make it more complex and leave the DOM messy. Sometimes, it might even come with performance issues. Removing is cleaner but it could be expensive if the element has to be re-created in the lifetime of the application. In that case, it is up to you to apply this simple rule and make your judgement based on your application structure and behavior.