Angular Directives

Directives are special instructions in Angular that tell the framework to do something to a DOM element — either change its appearance, add or remove it from the screen, or change its behavior. The word "directive" means "an instruction" — and that is exactly what Angular directives are: instructions given to the template.

Angular has three types of directives:

  • Structural Directives — Change the structure of the DOM by adding or removing elements.
  • Attribute Directives — Change the appearance or behavior of an existing element.
  • Component Directives — Components themselves are technically a type of directive with a template.

This topic focuses on structural and attribute directives, as these are the directives developers use most frequently.

Structural Directives

Structural directives change the HTML structure by adding, removing, or repeating elements. They always start with an asterisk *, which is Angular's shorthand for a more complex syntax that Angular handles internally.

*ngIf — Conditional Rendering

*ngIf adds an element to the DOM when a condition is true, and removes it when the condition is false. The element is not just hidden — it is completely removed from (or added back to) the page structure.


// visibility.component.ts
export class VisibilityComponent {
  isLoggedIn = false;
  hasError = true;
  itemCount = 3;

  toggleLogin() {
    this.isLoggedIn = !this.isLoggedIn;
  }
}

<!-- visibility.component.html -->

<button (click)="toggleLogin()">Toggle Login</button>

<div *ngIf="isLoggedIn">
  <p>Welcome back! You are logged in.</p>
</div>

<div *ngIf="!isLoggedIn">
  <p>Please log in to continue.</p>
</div>

<p *ngIf="hasError">An error occurred. Please try again.</p>

<p *ngIf="itemCount > 0">You have {{ itemCount }} items.</p>
Using ngIf with else

Angular provides an else option for *ngIf using the ng-template tag, which allows defining a fallback block without duplicating logic:


<div *ngIf="isLoggedIn; else notLoggedIn">
  <p>Welcome back!</p>
</div>

<ng-template #notLoggedIn>
  <p>Please log in to continue.</p>
</ng-template>

The #notLoggedIn creates a template reference for the ng-template block. When isLoggedIn is false, Angular renders the notLoggedIn template instead.

*ngFor — Repeating Elements

*ngFor repeats an HTML element for each item in a list. It is Angular's version of a "for each" loop — it creates one instance of the element for every item in the provided array.


// product-list.component.ts
export class ProductListComponent {
  products = [
    { id: 1, name: 'Laptop Stand', price: 39.99, inStock: true },
    { id: 2, name: 'Mechanical Keyboard', price: 89.99, inStock: true },
    { id: 3, name: 'Webcam HD', price: 65.00, inStock: false },
    { id: 4, name: 'USB-C Hub', price: 45.00, inStock: true }
  ];
}

<!-- product-list.component.html -->
<h3>Product List</h3>
<ul>
  <li *ngFor="let product of products">
    {{ product.name }} — ${{ product.price }}
  </li>
</ul>

Output on screen:


• Laptop Stand — $39.99
• Mechanical Keyboard — $89.99
• Webcam HD — $65
• USB-C Hub — $45
Using the Index in ngFor

Angular provides access to the current loop index using the index keyword:


<ul>
  <li *ngFor="let product of products; let i = index">
    {{ i + 1 }}. {{ product.name }} — ${{ product.price }}
  </li>
</ul>
Combining ngFor with ngIf

<ul>
  <li *ngFor="let product of products">
    {{ product.name }}
    <span *ngIf="!product.inStock">(Out of Stock)</span>
  </li>
</ul>
trackBy — Improving Performance

When the list data changes (items added or removed), Angular re-renders the list by default. The trackBy function tells Angular how to identify each item uniquely, so it only re-renders the items that actually changed:


<li *ngFor="let product of products; trackBy: trackById">
  {{ product.name }}
</li>

trackById(index: number, product: any): number {
  return product.id;
}

*ngSwitch — Multiple Conditional Blocks

*ngSwitch is used when there are multiple possible states and a different block should display for each one. It works like a switch statement in programming.


// status.component.ts
export class StatusComponent {
  orderStatus = 'shipped';   // Can be: 'pending', 'shipped', 'delivered'
}

<!-- status.component.html -->
<div [ngSwitch]="orderStatus">
  <p *ngSwitchCase="'pending'">Order is being prepared.</p>
  <p *ngSwitchCase="'shipped'">Order is on the way!</p>
  <p *ngSwitchCase="'delivered'">Order has been delivered.</p>
  <p *ngSwitchDefault>Unknown status.</p>
</div>

Because orderStatus is 'shipped', only the second paragraph appears on screen.

Attribute Directives

Attribute directives change the look or behavior of an element that already exists in the DOM. Unlike structural directives, they do not add or remove elements.

ngClass — Dynamic CSS Classes

ngClass adds or removes CSS classes on an element based on conditions. This is useful for highlighting selected items, showing error states, or changing element appearance based on data.


// alert.component.ts
export class AlertComponent {
  alertType = 'success';   // Can be: 'success', 'warning', 'error'
  isHighlighted = true;
}

<!-- alert.component.html -->

<!-- Object syntax: key is class name, value is condition -->
<div [ngClass]="{ 'alert-success': alertType === 'success',
                   'alert-error': alertType === 'error',
                   'highlighted': isHighlighted }">
  Alert message here
</div>

<!-- String syntax -->
<p [ngClass]="alertType">Status message</p>

<!-- Array syntax -->
<p [ngClass]="['base-class', alertType]">Status message</p>

ngStyle — Dynamic Inline Styles

ngStyle applies inline CSS styles to an element dynamically, based on values from the TypeScript class.


// style-demo.component.ts
export class StyleDemoComponent {
  fontSize = 18;
  fontColor = 'darkblue';
  isUrgent = true;
}

<!-- style-demo.component.html -->
<p [ngStyle]="{ 'font-size': fontSize + 'px', 'color': fontColor }">
  This text uses dynamic styles.
</p>

<p [ngStyle]="{ 'font-weight': isUrgent ? 'bold' : 'normal',
                 'color': isUrgent ? 'red' : 'black' }">
  Urgent message
</p>

Creating a Custom Attribute Directive

Angular allows creating custom directives for reusable behavior. Here is an example of a directive that highlights an element on mouse hover:


// highlight.directive.ts
import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[appHighlight]'   // Attribute selector
})
export class HighlightDirective {
  @Input() appHighlight = 'yellow';   // Accept a color input

  constructor(private el: ElementRef) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.el.nativeElement.style.backgroundColor = this.appHighlight;
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.el.nativeElement.style.backgroundColor = '';
  }
}

<!-- Using the custom directive -->
<p appHighlight="lightblue">Hover over this text to highlight it blue.</p>
<p appHighlight="lightgreen">Hover over this text to highlight it green.</p>
<p appHighlight>Hover over this text to highlight it yellow (default).</p>

The @Directive decorator defines this as a directive. ElementRef gives access to the actual DOM element. @HostListener listens for events on the host element (the element the directive is applied to).

Summary

Directives are instructions that tell Angular how to modify the DOM. Structural directives (*ngIf, *ngFor, *ngSwitch) add, remove, or repeat elements. Attribute directives (ngClass, ngStyle) change the appearance or behavior of existing elements. The asterisk * prefix on structural directives is Angular shorthand for template wrapping. Custom directives can be created using the @Directive decorator to encapsulate reusable DOM behavior. trackBy in *ngFor improves rendering performance for dynamic lists.

Leave a Comment

Your email address will not be published. Required fields are marked *