Angular Directives
Directives add behavior to existing elements and components.
Directive Essentials
- Add behavior to elements with
@Directiveand a selector. - Structural directives (
*ngIf,*ngFor) add/remove DOM. - Attribute directives (
[ngClass], custom[w3X]) change look/behavior without creating/removing nodes. - Star syntax (
*) is sugar that expands to<ng-template>. - Expose directive inputs with
@Input(); alias with@Input('alias')to bind via[alias].
*ngIf="condition"*ngFor="let item of items"@Directive({ selector: '[w3Highlight]' })<div w3Highlight></div>Notes:
- Related: SeeTemplates,Data Binding,Conditional Rendering, andLists.
Basic Directives
*ngIfshows/hides content based on a condition.*ngForrepeats a block for each list item.- Toggle a flag to add/remove the list; render items with
*ngFor.
<p *ngIf="items.length > 0">We have {{ items.length }} items</p><li *ngFor="let item of items">{{ item }}</li>Example
import { bootstrapApplication } from '@angular/platform-browser';import { Component } from '@angular/core';import { CommonModule } from '@angular/common';@Component({ selector: 'app-root', standalone: true, imports: [CommonModule], template: ` <h3>Directives</h3> <p *ngIf="items.length > 0">We have {{ items.length }} items</p> <ul> <li *ngFor="let item of items">{{ item }}</li> </ul> <button (click)="toggle()">Toggle Items</button> `})export class App { show = true; get items() { return this.show ? ['Angular', 'Components', 'Directives'] : []; } toggle() { this.show = !this.show; }}bootstrapApplication(App);<app-root></app-root>Example explained
- *ngIf: Adds/removes the paragraph from the DOM when the condition is truthy/falsy.
- *ngFor: Repeats the
<li>block for each item initems. - Toggle: The button flips
show, which changes the computeditemsgetter.
Notes:
- CommonModule required: Import
CommonModulewhen using built-in directives in standalone components. - Star syntax is sugar:
*ngIf/*ngForexpand to<ng-template>; the DOM nodes truly appear/disappear. - Avoid heavy work in templates: Don't call expensive functions in
*ngFor. Compute in the component instead. For long lists, seeLists and usetrackBy. - One structural per host: Don't put two
*directives on the same element. Wrap one in<ng-container>if needed.
ngIf with else
- Point
*ngIfto a fallback template withelse. - Use
then/elsesyntax to make both branches explicit. - Angular adds/removes DOM blocks entirely.
<ng-container *ngIf="loggedIn; else loggedOut"></ng-container><ng-template #loggedOut>...</ng-template><ng-container *ngIf="hasAccess; then accessTpl; else noAccessTpl"></ng-container>Example
import { bootstrapApplication } from '@angular/platform-browser';import { Component } from '@angular/core';import { CommonModule } from '@angular/common';@Component({ selector: 'app-root', standalone: true, imports: [CommonModule], template: ` <h3>ngIf with else</h3> <button (click)="loggedIn = !loggedIn"> {{ loggedIn ? 'Log out' : 'Log in' }} </button> <ng-container *ngIf="loggedIn; else loggedOut"> <p>Welcome back, {{ user }}!</p> </ng-container> <ng-template #loggedOut> <p>Please log in to continue.</p> </ng-template> <hr> <h4>ngIf then/else syntax</h4> <button (click)="hasAccess = !hasAccess"> Toggle Access ({{ hasAccess ? 'granted' : 'denied' }}) </button> <ng-container *ngIf="hasAccess; then accessTpl; else noAccessTpl"></ng-container> <ng-template #accessTpl> <p>Access granted.</p> </ng-template> <ng-template #noAccessTpl> <p>Access denied.</p> </ng-template> `})export class App { loggedIn = false; user = 'Angular User'; hasAccess = true;}bootstrapApplication(App);<app-root></app-root>Example explained
- *ngIf ... else: Renders the main block when the condition is true; otherwise renders the template referenced by
#loggedOut. - then/else: The
then/elseform makes both branches explicit via named templates. - State: Buttons toggle
loggedInandhasAccessto demonstrate both branches.
Notes:
- Template refs: The
#refyou pass toelse/thenreferences an<ng-template>. Remember this code won't exist in the DOM until rendered. - Avoid extra DOM: Use
<ng-container>to group structure without adding extra elements.
Attribute Directive (hover highlight)
- Runs on an existing element (no DOM created/destroyed).
- Changes appearance or behavior (e.g., add styles, classes, attributes).
- Example uses
@HostBindingand@HostListenerto set background on hover.
@Directive({ selector: '[w3Highlight]' })<div [w3Highlight]="'lightyellow'">Hover me</div>Example
import { bootstrapApplication } from '@angular/platform-browser';import { Component, Directive, Input, HostBinding, HostListener } from '@angular/core';import { CommonModule } from '@angular/common';@Directive({ selector: '[w3Highlight]', standalone: true})export class HighlightDirective { @Input('w3Highlight') highlightColor = 'lightyellow'; @HostBinding('style.transition') transition = 'background-color 150ms ease-in-out'; @HostBinding('style.backgroundColor') bg = ''; @HostListener('mouseenter') onEnter() { this.bg = this.highlightColor; } @HostListener('mouseleave') onLeave() { this.bg = ''; }}@Component({ selector: 'app-root', standalone: true, imports: [CommonModule, HighlightDirective], styles: [` .box { padding: 10px; border: 1px dashed #bbb; border-radius: 6px; } `], template: ` <h3>Attribute Directive (highlight)</h3> <p>Hover the first box to see the effect:</p> <div [w3Highlight]="'lightyellow'">I get highlighted on hover</div> <div>I do not</div> `})export class App {}bootstrapApplication(App);<app-root></app-root>Example explained
- [w3Highlight]="'lightyellow'": Passes a color into the directive via its input alias.
- @HostBinding: Binds the element's
style.backgroundColorand transition properties from directive fields. - @HostListener: Reacts to
mouseenter/mouseleaveto set/clear the background color.
Notes:
- Selector collisions: Use specific selectors for custom directives (e.g.,
[w3Highlight]) to avoid clashing with other libraries. - Performance: Keep host listeners light; avoid heavy synchronous work in
@HostListenerhandlers.

