A Practical Guide to Angular Directives

Share this article

A Practical Guide to Angular Directives

This article focuses on Angular directives — what are they, how to use them, and to build our own.

Directives are perhaps the most important bit of an Angular application, and if we think about it, the most-used Angular unit, the component, is actually a directive.

An Angular component isn’t more than a directive with a template. When we say that components are the building blocks of Angular applications, we’re actually saying that directives are the building blocks of Angular applications.

Basic overview

At the core, a directive is a function that executes whenever the Angular compiler finds it in the DOM. Angular directives are used to extend the power of the HTML by giving it new syntax. Each directive has a name — either one from the Angular predefined like ng-repeat, or a custom one which can be called anything. And each directive determines where it can be used: in an element, attribute, class or comment.

By default, from Angular versions 2 and onward, Angular directives are separated into three different types:

Components

As we saw earlier, components are just directives with templates. Under the hood, they use the directive API and give us a cleaner way to define them.

The other two directive types don’t have templates. Instead, they’re specifically tailored to DOM manipulation.

Attribute directives

Attribute directives manipulate the DOM by changing its behavior and appearance.

We use attribute directives to apply conditional style to elements, show or hide elements or dynamically change the behavior of a component according to a changing property.

Structural directives

These are specifically tailored to create and destroy DOM elements.

Some attribute directives — like hidden, which shows or hides an element — basically maintain the DOM as it is. But the structural Angular directives are much less DOM friendly, as they add or completely remove elements from the DOM. So, when using these, we have to be extra careful, since we’re actually changing the HTML structure.

Using the Existing Angular Directives

Using the existing directives in Angular is fairly easy, and if you’ve written an Angular application in the past, I’m pretty sure you’ve used them. The ngClass directive is a good example of an existing Angular attribute directive:

<p [ngClass]="{'blue'=true, 'yellow'=false}">
    Angular Directives Are Cool!
</p>

<style>
    .blue{color: blue}
    .yellow{color: yellow}
</style>

So, by using the ngClass directive on the example below, we’re actually adding the blue class to our paragraph, and explicitly not adding the yellow one. Since we’re changing the appearance of a class, and not changing the actual HTML structure, this is clearly an attribute directive. But Angular also offers out-of-the-box structural directives, like the ngIf:

@Component({
  selector: 'ng-if-simple',
  template: `
    <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button>
    show = {{show}}
    <br>
    <div *ngIf="show">Text to show</div>
`
})

class NgIfSimple {
  show: boolean = true;
}

In this example, we use the ngIf directive to add or remove the text using a button. In this case, the HTML structure itself is affected, so it’s clearly a structural directive.

For a complete list of available Angular directives, we can check the official documentation.

As we saw, using Angular directives is quite simple. The real power of Angular directives comes with the ability to create our own. Angular provides a clean and simple API for creating custom directives, and that’s what we’ll be looking at in the following sections.

Creating an attribute directive

Creating a directive is similar to creating a component. But in this case, we use the @Directive decorator. For our example, we’ll be creating a directive called “my-error-directive”, which will highlight in red the background of an element to indicate an error.

For our example, we’ll be using the Angular 2 quickstart package. We just have to clone the repository, then run npm install and npm start. It will provide us a boilerplate app that we can use to experiment. We’ll build our examples on top of that boilerplate.

Let’s start by creating a file called app.myerrordirective.ts on the src/app folder and adding the following code to it:

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

@Directive({
    selector:'[my-error]'
})

export class MyErrorDirective{
    constructor(elr:ElementRef){
        elr.nativeElement.style.background='red';
    }
}

After importing the Directive from @angular/core we can then use it. First, we need a selector, which gives a name to the directive. In this case, we call it my-error.

Best practice dictates that we always use a prefix when naming our Angular directives. This way, we’re sure to avoid conflicts with any standard HTML attributes. We also shouldn’t use the ng prefix. That one’s used by Angular, and we don’t want to confuse our custom created Angular directives with Angular predefined ones. In this example, our prefix is my-.

We then created a class, MyErrorDirective. To access any element of our DOM, we need to use ElementRef. Since it also belongs to the @angular/core package, it’s a simple matter of importing it together with the Directive and using it.

We then added the code to actually highlight the constructor of our class.

To be able to use this newly created directive, we need to add it to the declarations on the app.module.ts file:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyErrorDirective } from './app.myerrordirective';

import { AppComponent }  from './app.component';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent, MyErrorDirective ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

Finally, we want to make use of the directive we just created. To do that, let’s navigate to the app.component.ts file and add the following:

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

@Component({
  selector: 'my-app',
  template: `<h1 my-error>Hello {{name}}</h1>`,
})
export class AppComponent  { name = 'Angular'; }

The final result looks similar to this:

Angular directives: attribute directive example

Creating a structural directive

In the previous section, we saw how to create an attribute directive using Angular. The approach for creating a structural behavior is exactly the same. We create a new file with the code for our directive, then we add it to the declarations, and finally, we use it in our component.

For our structural directive, we’ll implement a copy of the ngIf directive. This way, we’ll not only be implementing a directive, but also taking a look at how Angular directives handle things behind the scenes.

Let’s start with our app.mycustomifdirective.ts file:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
    selector: '[myCustomIf]'
})

export class MyCustomIfDirective {

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

    @Input() set myCustomIf(condition: boolean) {
        if (condition) {
            this.viewContainer.createEmbeddedView(this.templateRef);
        } else {
            this.viewContainer.clear();
        }
    }
}

As we can see, we’re using a couple of different imports for this one, mainly: Input, TemplateRef and ViewContainerRef. The Input decorator is used to pass data to the component. The TemplateRef one is used to instantiate embedded views. An embedded view represents a part of a layout to be rendered, and it’s linked to a template. Finally, the ViewContainerRef is a container where one or more Views can be attached. Together, these components work as follows:

Directives get access to the view container by injecting a ViewContainerRef. Embedded views are created and attached to a view container by calling the ViewContainerRef’s createEmbeddedView method and passing in the template. We want to use the template our directive is attached to so we pass in the injected TemplateRef. — from Rangle.io’s Angular 2 Training

Next, we add it to our declarators:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyErrorDirective } from './app.myerrordirective';
import { MyCustomIfDirective } from './app.mycustomifdirective';

import { AppComponent }  from './app.component';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent, MyErrorDirective, MyCustomIfDirective ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

And we use it in our component:

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

@Component({
  selector: 'my-app',
  template: `<h1 my-error>Hello {{name}}</h1>
         <h2 *myCustomIf="condition">Hello {{name}}</h2>
             <button (click)="condition = !condition">Click</button>`,
})

export class AppComponent  {
    name = 'Angular';
    condition = false;    
}

The kind of approach provided by structural directives can be very useful, such as when we have to show different information for different users based on their permissions. For example, a site administrator should be able to see and edit everything, while a regular user shouldn’t. If we loaded private information into the DOM using an attribute directive, the regular user and all users for that matter would have access to it.

Angular Directives: Attribute vs Structural

We’ve looked at attribute and structural directives. But when should we use one or the other?

The answer might be confusing and we can end up using the wrong one just because it solves our problems. But there’s a simple rule that can help us choose the right one. Basically, if the element that has the directive will still be useful in the DOM when the DOM is not visible, then we should definitely keep it. In this case, we use an attribute directive like hidden. But if the element has no use, then we should remove it. However, we have to be careful to avoid some common pitfalls. We have to avoid the pitfall of always hiding elements just because it’s easier. This will make the DOM much more complex and probably have an impact on overall performance. The pitfall of always removing and recreating elements should also be avoided. It’s definitely cleaner, but comes at the expense of performance.

All in all, each case should be carefully analyzed, because the ideal solution is always the one that has the least overall impact on your application structure, behavior and performance. That solution might be either attribute directives, structural directives or, in the most common scenario, a compromise between both of them.

Conclusion

In this article, we took a look at Angular directives, the core of Angular applications. We looked at the different types of directives and saw how to create custom ones that suit our needs.

I hope that this article was able to get you up and running with Angular directives. If you have any questions, feel free to use the comment section below.

Frequently Asked Questions (FAQs) about Angular Directives

What are the different types of Angular Directives?

Angular Directives are classified into three types: Component Directives, Attribute Directives, and Structural Directives. Component Directives, as the name suggests, are directives with a template. They are essentially Angular components. Attribute Directives are used to change the behavior, look, and feel of a DOM element. Structural Directives, on the other hand, are used to manipulate the DOM layout by adding, removing, or replacing elements in the DOM.

How do I create a custom directive in Angular?

Creating a custom directive in Angular involves a few steps. First, you need to import the Directive decorator from the Angular core. Then, you need to define a directive class and decorate it with the @Directive decorator. The selector property in the decorator should match the name you want to use for your directive. Finally, you need to add your directive to the declarations array in your NgModule.

How do I use built-in directives in Angular?

Angular provides several built-in directives that you can use in your templates. For example, you can use the *ngIf directive to conditionally render elements, or the *ngFor directive to render a list of items. To use these directives, you simply add them to your template with the appropriate syntax.

What is the difference between a directive and a component in Angular?

In Angular, a component is a type of directive that has a template and is tied to a specific view. A directive, on the other hand, is a way to add behavior to an element in the DOM. While components are used to create UI widgets, directives are used to add behavior to existing elements.

How do I bind data to a directive in Angular?

Data binding in Angular directives can be done in several ways. One common way is through property binding, where you bind a property of a DOM element to a property of your component. You can also use event binding to respond to user actions, or two-way binding to keep your model and view in sync.

How do I test a directive in Angular?

Testing a directive in Angular involves creating a test component that uses the directive, and then testing that component. You can use the TestBed utility to create a dynamic test component, and then use the ComponentFixture to interact with the component and its directive.

Can I use multiple directives on a single element in Angular?

Yes, you can use multiple directives on a single element in Angular. However, you should be aware that the order in which directives are applied can affect the final result. Angular applies directives in the order they are listed in the template, so you should list your directives in the order you want them to be applied.

How do I pass parameters to a directive in Angular?

You can pass parameters to a directive in Angular using the @Input decorator. This allows you to bind a property of your directive to a value in your component. You can then use this value inside your directive to control its behavior.

How do I create a shared directive in Angular?

To create a shared directive in Angular, you need to define your directive in a shared module. You can then import this shared module into any other module where you want to use the directive. This allows you to reuse the same directive across multiple components or modules.

How do I debug a directive in Angular?

Debugging a directive in Angular can be done using the Angular DevTools extension for Chrome and Firefox. This tool allows you to inspect your application’s components, directives, and services, and see their current state and dependencies. You can also use the console to interact with your application and debug issues.

Claudio RibeiroClaudio Ribeiro
View Author

Cláudio Ribeiro is a software developer, traveler, and writer from Lisbon. He's the author of the book An IDE Called Vim. When he is not developing some cool feature at Kununu he is probably backpacking somewhere in the world or messing with some obscure framework.

angularangular-hubcomponentsdirectivesgitCSLearn Angular
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week