Custom Attribute Directives

Overview

In Angular, custom attribute directives are a fundamental building block for extending the functionality and behavior of your HTML elements. They allow you to create reusable, self-contained modules that can modify the appearance or behavior of DOM elements in your application. In this course chapter, we will dive deep into custom attribute directives, understanding how to create theme and leverage their power in your Angular applications.

Creating a Custom Attribute Directive Using Angular CLI

The Angular CLI simplifies the process of creating custom attribute directives. Let's create a directive named appSetBackground to change an element's background color when hovered over:

Step 1: Generate the Directive

In your terminal, navigate to your Angular project's root folder and user the Angular CLI to generate a new directive:

ng generate directive setBackground

This command will create the appSetBackground directive files in your project.

Step 2: Define the Directive Logic

Edit the set-background.directive.ts file created by the CLI. The directive class should look like this:

We have two ways of achieving our target:

Traditional Method (nativeElement property)

nativeElement property gives us the direct access to DOM bypassing the angular, it is good but not advisable. below are the few reasons for it:

  • Angular keeps the Component and view in Sync using Templates, data binding and change detection, etc. All of them bypassed when we update the DOM directly.
  • DOM manipulation works only in Browser. you will not able to use the App in other platforms like in web worker, in Server, or in a Desktop, or in mobile app etc. where there is no browser.
  • The DOM APIs do not sanitize the data. Hence it is possible to inject a script, thereby, opening our app an easy target for the XSS injection attack.

set-background.directive.ts:

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

@Directive({
  selector: '[appSetBackground]'
})
export class SetBackgroundDirective{

  constructor(el: ElementRef) { 
    el.nativeElement.style.backgroundColor = "#00ff00"; // This will turn the background color of the attached element to green.
  }

}

In the above code we written the code inside the constructor, that is not a good practice. so, instead of writing the code in constructor we can write it in ngOnInit method

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

@Directive({
  selector: '[appSetBackground]'
})
export class SetBackgroundDirective implements OnInit {

  constructor(private el: ElementRef) { 
    // el.nativeElement.style.backgroundColor = "#00ff00";
  }

  ngOnInit(){
    this.el.nativeElement.style.backgroundColor = "#00ff00";
  }

}

// Notice some changes
1. Implements OnInit interface for ngOnInit hook function
2. private el in the constructor, behind the scenes angular will create the private property with the name of el to be accessible throughout the class.

app.component.html:

<button appSetBackground>Click Me</button>

Using Renderer2 - It allows us to manipulate DOM without accessing the DOM elements directly. It provides a layer of abstraction between DOM element and component code.

set-background.directive.ts:

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

@Directive({
  selector: '[appSetBackground]'
})
export class SetBackgroundDirective implements OnInit {

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

  ngOnInit(){
    this.renderer.setStyle(this.element.nativeElement, "backgroundColor", "#00ff00");
  }

}
image-27.png

Step 3: Use the Directive

In your template, you can now use the appHighlight attribute to apply the directive:

<button appSetBackground>Click Me</button>

Now, you would observe the background of the element would change to green.

Creating a Custom Attribute Directive Manually

Manually creating a custom attribute directive allows you to have a deeper understanding of how it works. Let's create the same appSetBackground directive without using the Angular CLI:

Step 1: Create the Directive Files

Create a new TypeScript file for your directive, for example, set-background.directive.ts , and define the directive class:

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

@Directive({
  selector: '[appSetBackground]'
})
export class SetBackgroundDirective {

  constructor(private el: ElementRef, private renderer: Renderer2) { }
// same as above process
}

Step 2: Register the Directive

In your module file (e.g., app.module.ts), import the SetBackgroundDirective class and add it to the declarations array:

import { SetBackgroundDirective } from './set-background.directive';

@NgModule({
  declarations: [
    // ... other components and directives
    SetBackgroundDirective
  ],
  // ...
})
export class AppModule { }

Step 3: Use the Directive

You can now use the appSetBackground attribute to apply the directive in your template:

<p appHighlight>Hover over me!</p>