Custom Directives in Angular

Writing a custom directive in Angular

Rahul Kapoor
Geek Culture

--

Image showing Angular custom directives
Angular custom directives

Before we go ahead and write a custom directive in Angular, you must have a basic understanding of Built-in directives. Check out this article on built-in directives.

Once you are done with Built-in directives, let’s try to understand now 2 terminologies: HostListener and HostBinding which will be used while creating our own Attribute Directive.

HostListener

In Angular, the @HostListener() function decorator allows you to handle events of the host element in the directive class.

Let’s take the following requirement: when you click on a host element you want to show an alert window. To do this, you need to handle events raised on the host element in the directive class. In Angular, you do this using @HostListener().

HostBinding

In Angular, the @HostBinding() function decorator allows you to set the properties of the host element from the directive class.

Let’s say you want to change the style properties such as height, width, color, margin, border, etc., or any other internal properties of the host element in the directive class. Here, you’d need to use the @HostBinding() decorator function to access these properties on the host element and assign a value to it in the directive class.

The @HostBinding() decorator takes one parameter, the name of the host element property which value we want to assign in the directive.

Custom Attribute Directive

To create a custom attribute directive you need to add a class with a Directive decorator on it which can be imported from @angular/core and provide a selector to it, to uniquely use that directive throughout our application.

Code showing Directive which handles toggling of a dropdown
The Directive which handles toggling of a dropdown

As you can see in the code above, I have placed my directive in a separate shared folder (but you can place it anywhere in your app) and named it dropdown.directive.ts

Also, I have used an attribute selector as [app-dropdown] as I will be adding it as an attribute to my dropdown button.

HostBinding will bind our directive [app-dropdown] with the open class property where this property will dynamically change based on the value supplied to isOpen.

HostListener will listen to the (click) event and call toggleDropdown() method when the user clicks the element where the directive is placed. In our code, we are just toggling the open class property to true and false thus opening and closing our dropdown.

Now to use it in our application we need to let Angular know that a custom directive has been added by us. We declare the class DropdownDirective in our app.module.ts inside the declarations array which contains declarations of components and directives.

code showing placing of custom directive
code showing placing of custom directive

If you run your application and click the dropdown button it will open and close the dropdown menu.

Custom Structural Directive

Structural directives are responsible for HTML layout. We can add and remove elements in the DOM layout dynamically. The HTML element using the directive is called the host element for that directive. The effect of the directive is only on the host element and its descendants. To add and remove host elements from the DOM layout we can use TemplateRef and ViewContainerRef classes in our structural directive.

Let’s try to understand it with an example:

We will create a button that will toggle hide and show of heading for us. Now we can directly use property in the .ts class and then use a built-in ngIf structural directive to handle this. But let’s see how to do it by writing our own structural directive.

First, create the property in the .ts class and write the logic for the (click) event of the button. Refer code snapshot below:

AppComponent code
AppComponent code

showContent property we will use to hide or show our data. onShowContent method we will bind the (click) event of our button for toggling in our template HTML.

AppComponent template HTML code
AppComponent template HTML code

We can use structural directive with either * or ng-template. Here in my example, I will be using ng-template (as highlighted in the code above).

Both my opening and closing <ng-template> tags have a div container with the heading placed inside it which we want to toggle with the button -> Toggle to view/hide content.

Before you get confused with the below logic

<ng-template [appContent]="showContent">

being placed inside the ng-template tag, let’s first jump on to our custom structural directive and then come back, hopefully, things will get cleared.

I have created a custom directive with the name ContentDirective. As seen above in the custom attribute directive we similarly place the Directive decorator on the class of ContentDirective and provide a unique selector to it.

Now create a setter method decorated with @Input().

Note: We need to take care that the method name should be the same as the directive name.

The method takes condition as an argument on which we can place our IF logic and render the content accordingly. Kindly refer to the code screenshot below.

ContentDirective code
ContentDirective code

To change the DOM layout we should use TemplateRef and ViewContainerRef in our structural directive.
TemplateRef: It represents an embedded template that can be used to instantiate embedded views.
ViewContainerRef: It represents a container where one or more views can be attached.

To use the above classes in our directive, first, we need to instantiate them. Instantiate these classes using dependency injection in the constructor as follows.

constructor( private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }

To add a host element in DOM layout, we need to call createEmbeddedView() a method of ViewContainerRef. Find the line of code.

this.viewContainer.createEmbeddedView(this.templateRef);

If we want to clear view container, call clear() method of ViewContainerRef as given below.

this.viewContainer.clear();

To let Angular know about our custom directive we have to declare it in the declarations along with our components in the AppModule file (as shown in the code below).

Directive declared in AppModule file
Directive declared in AppModule file

Let’s go back to the <ng-template> tag where we placed some logic within the tag:

<ng-template [appContent]="showContent">

Here [appContent] is our custom directive selector and showContent is the property being passed which reaches the directive in the method parameter — condition.

Once you are done with all this run the application using ng serve and on toggling the button you can see the hide/show of your template.

--

--