All articles

Angular Directives Overview with Examples

The directive landscape on Angular 20: @if/@for/@switch control flow, ngClass/ngStyle/ngModel, and building a custom attribute directive with HostListener and input().

0 · log in to like, save & follow Share on LinkedIn Share on X
Angular Directives Overview with Examples

Components own a patch of screen; directives attach behavior to elements that already exist. Angular's template control flow (@if, @for, @switch), the attribute directives (ngClass, ngStyle, ngModel), and your own custom directives cover the "make this element behave differently" space. This overview maps the landscape on Angular 20 with a working demo — including a custom hover-highlight directive — all verified with ng build.

Angular Directives Overview with Examples

Control flow: the modern structural directives

What *ngIf/*ngFor did, built-in block syntax now does better:

<ul>
  @for (p of visibleProducts(); track p.sku) {
    <li>{{ p.name }} — {{ p.stock }} left</li>
  } @empty {
    <li>No products match.</li>
  }
</ul>

@switch (status()) {
  @case ('loading') { <p>Loading…</p> }
  @case ('ready')   { <p>{{ visibleProducts().length }} products shown</p> }
  @default          { <p>Something went wrong.</p> }
}

Why the new syntax won: no CommonModule import, sharper type narrowing inside @if, an @empty block *ngFor never had, and mandatory track. That last one is the quiet performance fix — without a track expression, replacing an array re-creates every DOM node; with track p.sku, Angular moves and updates only what changed. The old *ngFor made tracking optional and a decade of apps paid for it. (@if also replaces the *ngIf="x as y" idiom with @if (expr; as y).)

Migration is mechanical — ng generate @angular/core:control-flow converts an existing codebase.

Attribute directives: styling and forms

Attribute directives modify the element they sit on:

<li [ngClass]="{ 'sold-out': p.stock === 0 }"
    [ngStyle]="{ fontWeight: p.stock === 0 ? '400' : '600' }">
  • ngClass toggles classes from an object/array — for one class, plain [class.sold-out]="p.stock === 0" is lighter and needs no import.
  • ngStyle sets inline styles from an object; same story — single styles read better as [style.fontWeight]="...".
  • ngModel (from FormsModule) two-way binds form controls:
<label><input type="checkbox" [(ngModel)]="showSoldOut" /> show sold-out</label>

The banana-in-a-box [(ngModel)] is sugar for a property binding plus event binding — fine for simple forms; reactive forms take over when validation gets real.

Writing a custom directive

The demo's appHighlight colors any element on hover:

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

@Directive({ selector: '[appHighlight]' })
export class HighlightDirective {
  private readonly el = inject(ElementRef<HTMLElement>);
  readonly appHighlight = input<string>('#fff3bf');   // configurable color

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

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

Usage — import it like a component, apply it like an attribute:

<li appHighlight="#d3f9d8" [ngClass]="...">{{ p.name }}</li>

The anatomy: an attribute selector [appHighlight]; ElementRef for the host element via inject(); @HostListener for host events (there's a host: {} metadata alternative); and an input() named after the selector, which is what lets appHighlight="#d3f9d8" pass the color and apply the directive in one attribute. Directives compose — the same <li> carries appHighlight, ngClass, and ngStyle, each adding one behavior; that's the design intent, small orthogonal behaviors rather than mega-components.

Real-world custom directive material: autofocus on appear, click-outside detection, permission-based hiding (*appHasRole="'admin'"), input masks, tooltip attachment. The rule of thumb: behavior without markup = directive; behavior with markup = component.

Structural directives, the custom kind

You can still write custom structural directives (the * kind that add/remove template chunks) with TemplateRef and ViewContainerRef — permission gates being the classic. With @if covering conditionals and @defer covering lazy rendering, reach for a custom structural directive only when you truly need template-manipulation semantics the blocks don't express; most cases today are an @if around a computed().

Picking the right tool

Need Reach for
Conditional/repeated markup @if / @for (with track)
Toggle one class or style [class.x] / [style.y]
Multiple classes/styles from state ngClass / ngStyle
Form field binding [(ngModel)], then reactive forms
Reusable element behavior custom attribute directive
Reusable UI with its own markup a component

The demo component with all of the above — control-flow blocks, the checkbox filter, and the custom highlight directive — builds and runs in the companion repository (npm install && ng serve), alongside the lifecycle and nested-component samples from this series.

Enjoyed this article? Get the best GeeksArray articles in your inbox — once a week, no spam, unsubscribe anytime.

Comments (0)

Log in to join the conversation.

No comments yet — be the first to share your thoughts.