Guide to Angular Directives

Exploring Angular Directives

A directive class can modify the structure or behavior of elements in the DOM. Directives are one of the core building blocks of Angular applications and are used to create reusable, custom behaviors that can be applied to elements in a declarative manner.

1. Structural Directives:

Structural directives are used to modify the structure of the DOM by adding or removing elements.

a. ngIf:

Conditionally includes a template based on the truthiness of an expression.

  • Usage:

      <div *ngIf="isVisible">
        This will only be shown if isVisible is true.
      </div>
    

b. ngFor:

Repeats a portion of the DOM for each item in a list.

  • Usage:

      ul>
        <li *ngFor="let item of items">{{ item }}</li>
      </ul>
    

c. ngSwitch, ngSwitchCase, ngSwitchDefault:

Switches between multiple views based on a value.

  • Usage:

      <div [ngSwitch]="status">
        <div *ngSwitchCase="'online'">Online</div>
        <div *ngSwitchCase="'offline'">Offline</div>
        <div *ngSwitchDefault>Unknown Status</div>
      </div>
    

2. Attribute Directives:

Attribute directives change the appearance or behavior of an element, component, or another directive without altering its structure.

a. ngClass:

Dynamically adds or removes CSS classes.

  • Usage:

      <div [ngClass]="{'active': isActive, 'inactive': !isActive}">
        This div's class changes based on isActive.
      </div>
    

b. ngStyle:

Dynamically sets inline styles on an element.

  • Usage:

      <div [ngStyle]="{'background-color': isActive ? 'green' : 'red'}">
        This div's background color changes dynamically.
      </div>
    

c. ngModel:

Two-way data binding between input fields and component data. This is commonly used in forms.

  • Usage:

      <input [(ngModel)]="username" placeholder="Enter your username" />
      <p>Your username is: {{ username }}</p>
    

d. ngModelGroup:

Used to group controls together inside a form for validation and management.

  • Usage:

      <form>
        <div ngModelGroup="address">
          <input [(ngModel)]="street" name="street" placeholder="Street" />
          <input [(ngModel)]="city" name="city" placeholder="City" />
        </div>
      </form>
    

e. ngForm:

Used to create and manage forms. It's applied automatically when using <form>.

  • Usage:

      <form #form="ngForm">
        <input name="username" ngModel required />
        <button [disabled]="!form.valid">Submit</button>
      </form>
    

f. ngSubmit:

Listens for the submit event of a form.

  • Usage:

      <form (ngSubmit)="onSubmit()">
        <input name="username" ngModel required />
        <button type="submit">Submit</button>
      </form>
    

g. ngModelOptions:

Specifies options for ngModel, such as debounce time or event types for updating the model.

  • Usage:

      <input [(ngModel)]="username" [ngModelOptions]="{updateOn: 'blur'}" />
    

3. Other Common Directives:

a. ngClassOdd & ngClassEven:

Applies different classes to odd and even items in a loop.

  • Usage:

      <ul>
        <li *ngFor="let item of items; let i = index"
            [ngClass]="{'odd-class': i % 2 === 1, 'even-class': i % 2 === 0}">
          {{ item }}
        </li>
      </ul>
    

b. ngPlural & ngPluralCase:

They are used to display different templates based on a number's value.

  • Usage:

      <ng-container [ngPlural]="count">
        <ng-template ngPluralCase="=0">No items found.</ng-template>
        <ng-template ngPluralCase="=1">One item found.</ng-template>
        <ng-template ngPluralCase="other">{{ count }} items found.</ng-template>
      </ng-container>
    

4. Built-in Async Handling Directives:

a. ngIf with async:

You can use ngIf with the async pipe to handle asynchronous data (like Observables).

  • Usage:

      <div *ngIf="data$ | async as data">
        {{ data }}
      </div>
    

b. ngTemplateOutlet:

Dynamically renders a template inside a host element.

  • Usage:

      <ng-template #template let-name="name">
        <div>Hello, {{ name }}!</div>
      </ng-template>
    
      <ng-container [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{name: 'Angular'}"></ng-container>
    

5. Special Angular Directives for Forms:

a. ngFormControl:

An older directive used in Angular forms for model-driven forms. It was replaced by formControl in newer versions of Angular.

b. formControl:

Used in reactive forms to bind a form control to an input field.

  • Usage:

      <form [formGroup]="myForm">
        <input formControlName="name" />
      </form>
    

c. formControlName:

This is used with reactive forms to bind a specific control within a FormGroup.

  • Usage:

      <form [formGroup]="profileForm">
        <input formControlName="firstName" />
      </form>
    

d. formArrayName:

Used in reactive forms for arrays of controls.

  • Usage:

      <div formArrayName="aliases">
        <div *ngFor="let alias of aliases.controls; let i=index">
          <input [formControlName]="i" placeholder="Alias {{ i + 1 }}">
        </div>
      </div>
    

Summary of Built-in Angular Directives:

DirectiveTypePurpose
ngIfStructuralConditionally include or remove elements
ngForStructuralLoop over a collection and repeat elements
ngSwitchStructuralSwitch between multiple views
ngClassAttributeDynamically add/remove classes
ngStyleAttributeDynamically set inline styles
ngModelAttributeTwo-way data binding in forms
ngModelGroupAttributeGrouping controls in template-driven forms
ngSubmitAttributeSubmit event listener for forms
ngTemplateOutletAttributeRender a template dynamically

These built-in directives allow Angular to be powerful and flexible when controlling the structure, styling, and behavior of DOM elements and form controls


Custom Directive

Custom directives in Angular are a powerful tool to encapsulate reusable behavior that can be applied to DOM elements. They allow you to manipulate the DOM in ways specific to your application's needs without modifying the HTML structure.

Here are examples of custom directives along with common use cases where custom directives can be highly useful.


1. Highlight Text on Hover

This directive changes the background color of an element when hovered, similar to CSS :hover, but with more control and flexibility.

Use Case:

  • Highlighting specific elements (like buttons or cards) when hovered for better user interaction.

Example:

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

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

  @HostListener('mouseenter') onMouseEnter() {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'yellow');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'transparent');
  }
}

Usage in Template:

<div appHoverHighlight>
  Hover over this to see the highlight!
</div>

2. Disable Element Based on Condition

This directive disables or enables a form element based on a condition passed from the component.

Use Case:

  • You can use this directive to conditionally disable buttons or input fields based on form validation, user roles, or other conditions.

Example:

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

@Directive({
  selector: '[appDisable]'
})
export class DisableDirective implements OnChanges {
  @Input() appDisable: boolean;

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

  ngOnChanges() {
    if (this.appDisable) {
      this.renderer.setAttribute(this.el.nativeElement, 'disabled', 'true');
    } else {
      this.renderer.removeAttribute(this.el.nativeElement, 'disabled');
    }
  }
}

Usage in Template:

<button [appDisable]="isDisabled">Submit</button>

3. Auto Focus Input Field

This directive automatically focuses on an input field when the view is initialized.

Use Case:

  • Useful for forms where you want the first input field to be automatically focused when a form or dialog opens.

Example:

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

@Directive({
  selector: '[appAutoFocus]'
})
export class AutoFocusDirective implements AfterViewInit {
  constructor(private el: ElementRef) {}

  ngAfterViewInit() {
    this.el.nativeElement.focus();
  }
}

Usage in Template:

<input appAutoFocus placeholder="This input will be focused automatically">

4. Custom Tooltip

This directive adds a simple custom tooltip to any element.

Use Case:

  • Useful when you need to display additional information or tooltips on hover, but want more control over the tooltip's appearance than CSS alone provides.

Example:

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

@Directive({
  selector: '[appTooltip]'
})
export class TooltipDirective {
  @Input() appTooltip: string;
  tooltipElement: HTMLElement;

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

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

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

  private createTooltip() {
    this.tooltipElement = this.renderer.createElement('span');
    this.renderer.appendChild(
      this.tooltipElement,
      this.renderer.createText(this.appTooltip)
    );
    this.renderer.appendChild(this.el.nativeElement, this.tooltipElement);
    this.renderer.setStyle(this.tooltipElement, 'position', 'absolute');
    this.renderer.setStyle(this.tooltipElement, 'background', 'gray');
    this.renderer.setStyle(this.tooltipElement, 'color', 'white');
    this.renderer.setStyle(this.tooltipElement, 'padding', '5px');
  }

  private removeTooltip() {
    this.renderer.removeChild(this.el.nativeElement, this.tooltipElement);
  }
}

Usage in Template:

<button appTooltip="Click to Submit">Hover over me!</button>

5. Copy Text to Clipboard

This directive allows you to copy text to the clipboard when an element is clicked.

Use Case:

  • Commonly used in applications where users need to copy specific data (like API keys, codes, or URLs) to their clipboard with a single click.

Example:

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

@Directive({
  selector: '[appCopyToClipboard]'
})
export class CopyToClipboardDirective {
  @Input('appCopyToClipboard') textToCopy: string;

  @HostListener('click') copyText() {
    navigator.clipboard.writeText(this.textToCopy).then(() => {
      console.log('Text copied to clipboard: ' + this.textToCopy);
    });
  }
}

Usage in Template:

<button [appCopyToClipboard]="textToCopy">Copy Text</button>

6. Input Validator

This directive validates an input field for allowed characters, such as restricting input to numbers only.

Use Case:

  • Useful in forms where you need to restrict user input (e.g., allowing only numeric input for phone numbers or age).

Example:

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

@Directive({
  selector: '[appOnlyNumbers]'
})
export class OnlyNumbersDirective {

  @HostListener('keydown', ['$event'])
  onKeyDown(event: KeyboardEvent) {
    const allowedKeys = ['Backspace', 'Tab', 'End', 'Home', 'Delete'];
    if (
      allowedKeys.indexOf(event.key) !== -1 || // Allow control keys
      (event.key >= '0' && event.key <= '9') // Allow numbers
    ) {
      return;
    }
    event.preventDefault();
  }
}

Usage in Template:

<input appOnlyNumbers placeholder="Only numbers allowed">

Summary of Major Use Cases:

  1. Hover or Focus Effects: Directives like appHoverHighlight add dynamic behaviors that go beyond basic CSS.

  2. Conditional Enabling/Disabling: Directives like appDisable can be used to control element states dynamically.

  3. Auto-Focus Behavior: Automatically focusing on input fields for better UX in forms.

  4. Custom Tooltips: Displaying additional information on hover with custom styling and logic.

  5. Clipboard Copying: Simplifying the process of copying text to the clipboard.

  6. Input Validation: Enforcing restrictions on user input such as allowing only numbers or specific patterns.

Why Use Custom Directives?

  • Reusability: Custom directives can be reused throughout the application to enforce consistent behavior or style.

  • Separation of Concerns: Encapsulates logic in a directive rather than putting everything in the component.

  • Declarative Approach: Directives allow behavior to be declared directly in the template, making it cleaner and easier to maintain.