Angular Custom Directives Complete Guide

 Last Update:2025-06-23T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    8 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of Angular Custom Directives

Angular Custom Directives: Explanation and Important Information

Creating a Custom Directive

To create a custom directive in Angular, you need to use the Angular CLI or manually generate a directive file. The Angular CLI provides a convenient way to scaffold a directive:

ng generate directive highlight

This command creates a directive file named highlight.directive.ts with the necessary boilerplate code.

Directive Structure

A directive consists of a TypeScript class decorated with the @Directive decorator, which is imported from @angular/core. The @Directive decorator is configured with a metadata object that specifies the directive's selector and other properties.

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

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) {}

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

  @HostListener('mouseleave')
  onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string) {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', color);
  }
}

Key Components of a Directive

  • Selector: Specifies how the directive can be used in a template. It can be an attribute, a class, or an element.

    selector: '[appHighlight]' // Attribute selector
    selector: '.appHighlight' // Class selector
    selector: 'appHighlight'  // Element selector
    
  • HostListener: Binds an event handler to a host element event.

    @HostListener('mouseenter') onMouseEnter() {
      // Event handling logic here
    }
    
  • Renderer2: Provides methods to manipulate the DOM in a performant way without directly accessing it.

    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'yellow');
    
  • ElementRef: Represents a reference to the host DOM element.

    constructor(private el: ElementRef) {}
    
  • @Input and @Output: Allows communication between the directive and the parent component.

    @Input('appHighlight') highlightColor: string;
    @Output() highlightEvent = new EventEmitter<string>();
    

Registering Directives

Directives must be registered in an Angular module before they can be used. Registering a directive involves adding it to the declarations array of the module's metadata:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HighlightDirective } from './highlight.directive';

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

Using Directives in Templates

Once registered, the directive can be used in the component templates. For example, using the HighlightDirective defined earlier:

<p appHighlight>Highlight me on mouseover!</p>

This directive can be extended to accept input properties for more dynamic behavior:

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement Angular Custom Directives

Complete Examples, Step by Step for Beginners: Angular Custom Directives

Prerequisites:

  1. Node.js & npm: Ensure these are installed on your machine.
  2. Angular CLI: Install it using npm install -g @angular/cli.
  3. Basic knowledge of Angular: Familiarity with components, templates, modules, and TypeScript.

Example 1: Attribute Directive to Change Background Color

Step 1: Generate a New Directive

ng generate directive backgroundChanger

or simply

ng g d backgroundChanger

This command will create four files:

  1. background-changer.directive.ts
  2. background-changer.directive.spec.ts
  3. A reference to the directive in your application module (e.g., app.module.ts).

Step 2: Implement the Directive Logic

Open the background-changer.directive.ts file and implement the logic to change the background color of an element.

// src/app/background-changer.directive.ts
import { Directive, ElementRef, Input } from '@angular/core';

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

export class BackgroundChangerDirective {
  
  // Allow an input called background-color
  @Input('appBackgroundChanger') backgroundColor: string = '';

  constructor(private el: ElementRef) {
    // Initially, no background color is set
  }

  // Lifecycle hook which changes the background color when a new value is binding
  ngOnChanges() {
    if (this.backgroundColor) {
      this.el.nativeElement.style.backgroundColor = this.backgroundColor;
    }
  }
}

In this directive, @Input('appBackgroundChanger') backgroundColor binds the property backgroundColor to the directive's selector name. This allows you to pass a value to the directive from a component template, like so [appBackgroundChanger]="someColor".

Step 3: Use the Directive in a Component Template

Let’s use the BackgroundChangerDirective in a SimpleComponent.

First, generate a SimpleComponent using the Angular CLI:

ng generate component simple

Then, update the simple.component.html to include some elements with the directive applied.

<!-- src/app/simple/simple.component.html -->
<div [appBackgroundChanger]="'lightblue'">This div has a light blue background.</div>
<p [appBackgroundChanger]="colorFromComponent">This paragraph has a dynamically set background color.</p>
<button (click)="toggleColor()">Toggle Color</button>

Next, define the corresponding logic in simple.component.ts.

// src/app/simple/simple.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-simple',
  templateUrl: './simple.component.html',
  styleUrls: ['./simple.component.css']
})
export class SimpleComponent {

  colorFromComponent: string = 'yellow'; // Initial background color

  toggleColor() {
    // Toggle between yellow and green on click
    this.colorFromComponent = this.colorFromComponent === 'yellow' ? 'green' : 'yellow';
  }
}

Now, whenever you run your Angular app, the elements with the [appBackgroundChanger] directive on them will have their background colors set according to the provided values.


Example 2: Structural Directive to Conditionally Render Content

Structural directives alter the DOM layout by adding and removing elements. A common example of a structural directive is *ngIf. Now let's create one called *appUnless, which does the opposite of *ngIf.

Step 1: Generate a New Directive

ng generate directive unless

or

ng g d unless

Step 2: Implement the Structural Directive Logic

Modify the unless.directive.ts file similar to the attribute directive but with a slight twist for structural purposes.

// src/app/unless.directive.ts
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appUnless]'
})
export class UnlessDirective {

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

  // Decorate method with @Input decorator to listen for changes to the condition
  @Input()
  set appUnless(condition: boolean) {
    if (!condition) {
      this.viewContainer.createEmbeddedView(this.templateRef);
    } else {
      this.viewContainer.clear();
    }
  }

}

Explanation:

  • TemplateRef represents an internal document tree template.
  • ViewContainerRef provides the means to insert template elements into the DOM.

The set appUnless(condition: boolean) method listens for changes to the appUnless input property. If the condition evaluates to false, it inserts the template into the DOM; otherwise, it clears the DOM of this template.

Step 3: Use the Structural Directive

Let's use our custom structural directive *appUnless in the app.component.html.

<!-- src/app/app.component.html -->
<p *ngIf="isVisible">
  The paragraph is visible because the condition is true.
</p>

<p *appUnless="isVisible">
  The paragraph will be visible if the condition is NOT true, but here it is false.
</p>

<button (click)="toggleVisibility()">Toggle Visibility</button>

Modify the app.component.ts to handle the button click event and toggle visibility.

// src/app/app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  isVisible: boolean = true;

  toggleVisibility() {
    this.isVisible = !this.isVisible;
  }
}

When you run your app, the paragraph with *appUnless will only be rendered if isVisible is false.


Step 4: Compile & Serve the Application

Run your Angular application:

ng serve

Navigate to http://localhost:4200/ in your web browser, and you should see the effects of your new custom directives!


Summary

By following these step-by-step examples, you've created both an attribute (BackgroundChangerDirective) and a structural (UnlessDirective) custom directive in Angular. You can leverage these concepts to build more advanced behavior and modify the DOM as required by your application’s requirements.


Top 10 Interview Questions & Answers on Angular Custom Directives

1. What is an Angular Custom Directive?

Answer: An Angular Custom Directive is a user-defined attribute, element, class, or comment that can manipulate the DOM (Document Object Model). It allows you to extend HTML with custom behavior and functionality. Directives can be created to encapsulate complex logic, make repetitive tasks easier, and maintain clean, reusable code.

2. What are the types of Directives in Angular?

Answer: Angular has three main types of directives:

  • Attribute Directives: Change the appearance or behavior of an element, component, or another directive. For example, ngStyle and ngClass.
  • Structural Directives: Alter the layout by adding or removing DOM elements. The built-in structural directives include *ngIf, *ngFor, and *ngSwitch.
  • Component Directives: Classy directives that define a component. They combine HTML, CSS, and TypeScript to build complex UI controls.

3. How do you create a custom Attribute Directive in Angular?

Answer: To create a custom Attribute Directive, you define a class and use the @Directive decorator from the @angular/core module. Here’s a simple example:

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

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) {}

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

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string | null) {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', color);
  }
}

4. What does the @HostListener decorator do in Angular Directives?

Answer: The @HostListener decorator in Angular is used to subscribe to events emitted by the host element of the directive. It takes two arguments: the name of the event to listen for (like 'click' or 'mouseenter') and a callback method to trigger when the event is emitted.

5. How can you pass values to a custom Directive?

Answer: Values can be passed into custom Directives using @Input bindings. Here’s an example where the directive accepts a color value:

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

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  @Input('appHighlight') highlightColor: string;

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

  ngAfterViewInit() {
    this.highlight(this.highlightColor);
  }

  private highlight(color: string) {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', color);
  }
}

In the template, it can be used like this:

<p appHighlight="blue">This text has a blue background!</p>

6. Can you create Structural Directives in Angular?

Answer: Yes, you can create custom Structural Directives. Here’s an example of a Structural Directive that only displays content if the user is an admin:

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

@Directive({
  selector: '[appAdminOnly]'
})
export class AdminOnlyDirective {
  private hasView = false;

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

  @Input()
  set appAdminOnly(condition: boolean) {
    if (!this.hasView && condition) {
      this.viewContainer.createEmbeddedView(this.templateRef);
      this.hasView = true;
    } else if (this.hasView && !condition) {
      this.viewContainer.clear();
      this.hasView = false;
    }
  }
}

7. What is the difference between ElementRef and Renderer2 in Angular Directives?

Answer:

  • ElementRef: Provides direct access to the DOM element that the directive is attached to. Use it to interact with the DOM, but it is generally recommended to use Renderer2 for performance reasons.
  • Renderer2: A more secure and performant API for manipulating the DOM. It abstracts away direct DOM interaction, making your directives more testable and closer to the Angular philosophy of maintaining a clean separation between the view and the model.

8. How do you debug Angular Directives?

Answer: Debugging Angular Directives can be done using console logs, the browser's developer tools, and Angular's debugging environment. Here are a few tips:

  • Use console.log() to print the state of properties and variables.
  • Utilize breakpoints in your code to step through execution.
  • Consider leveraging the Angular DevTools in Chrome, which provides insight into the application and its components.

9. What is the significance of the ngOnChanges lifecycle hook in Directives?

Answer: The ngOnChanges lifecycle hook is crucial for responding to changes in input properties. This hook is called before ngOnInit (if the directive is a component) and during every subsequent change detection run, provided the input properties have changed. It receives an object with the previous and current values of the changed properties.

10. Can you provide an example of a Multi-Slot Custom Structural Directive in Angular?

Answer: Creating a structural directive with multiple slots involves using Angular’s TemplateRef and ViewContainerRef along with the *ngTemplateOutlet directive. Here’s an example:

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

@Directive({
  selector: '[appTemplateOut]'
})
export class TemplateOutDirective {
  @Input('appTemplateOut') templateInputs: { [key: string]: any };

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

  @Input()
  set appTemplateOut(condition: boolean) {
    if (condition) {
      this.viewContainer.createEmbeddedView(
        this.templateRef,
        this.templateInputs || {}
      );
    } else {
      this.viewContainer.clear();
    }
  }
}

Used in the template:

<ng-template #myMultiTemplate let-user="user" let-role="role">
  <div>{{ user }}, {{ role }}</div>
</ng-template>

<div *appTemplateOut="true; appTemplateOut: { user: 'John', role: 'Admin' }"></div>

This directive displays the bound content based on the condition, passing in multiple context variables to the template.

You May Like This Related .NET Topic

Login to post a comment.