Directives are one of the most powerful and frequently used features in Angular. They allow you to change layout, style, behavior, and structure directly from your templates.
This guide covers four essential Angular building blocks:
ngClass
ngStyle
ngSwitch
Async Pipe
Designed for beginners learning Angular as well as developers working on real-world systems.
1. What Are Angular Directives?
A directive is a class that adds behavior to elements in the template.
Three broad categories:
Component Directives
Comes with a template.
Structural Directives
Change the DOM layout.
Example: *ngIf, *ngFor, *ngSwitchCase
Attribute Directives
Change the appearance or behavior of an element.
Example: ngClass, ngStyle
We’ll focus on the commonly used attribute and structural directives.
2. ngClass — Apply Classes Dynamically
Use ngClass when you want to apply CSS classes conditionally.
Example 1: Simple Binding
<div [ngClass]="'highlight'">Hello</div>
Example 2: Conditional Classes
<div [ngClass]="{ 'active': isActive, 'disabled': !isActive }">
User Status
</div>
Example 3: Multiple Classes
<div [ngClass]="['box', theme]"></div>
Real-World Case: Status Badge
<span [ngClass]="{
'success': order.status === 'Delivered',
'warning': order.status === 'Pending',
'error': order.status === 'Cancelled'
}">
{{ order.status }}
</span>
3. ngStyle — Apply Inline Styles Conditionally
ngStyle helps you apply inline CSS dynamically.
Example 1: Single Style
<div [ngStyle]="{ 'color': 'red' }">Alert</div>
Example 2: Dynamic Value
<div [ngStyle]="{ 'font-size': size + 'px' }"></div>
Example 3: Conditional Styling
<div [ngStyle]="{
'background-color': value > 0 ? 'lightgreen' : 'salmon'
}">
{{ value }}
</div>
Real-World Case: Display Stock Status
<span [ngStyle]="{
'color': stock > 10 ? 'green' : 'orange'
}">
{{ stock }} items available
</span>
4. ngSwitch — Conditional Rendering for Multiple Conditions
Use ngSwitch when you have multiple possible views based on a single value.
Basic Example
<div [ngSwitch]="role">
<p *ngSwitchCase="'admin'">Admin Panel</p>
<p *ngSwitchCase="'user'">User Dashboard</p>
<p *ngSwitchDefault>Unknown Role</p>
</div>
Real-World Case: Payment Status
<div [ngSwitch]="payment.status">
<span *ngSwitchCase="'paid'">Payment Successful</span>
<span *ngSwitchCase="'failed'">Payment Failed</span>
<span *ngSwitchCase="'pending'">Payment Pending</span>
<span *ngSwitchDefault>Status Not Available</span>
</div>
This is cleaner and more readable than multiple *ngIf blocks.
5. Async Pipe — One of Angular’s Most Useful Tools
The Async Pipe is used to subscribe to Observables or Promises directly from the template.
Without Async Pipe, you must manually:
subscribe
unsubscribe
store values
Async Pipe handles all of this automatically.
Why Use Async Pipe?
Benefits
No memory leaks (auto unsubscribe)
Cleaner code
No manual subscription logic in TypeScript
Works perfectly with HttpClient, RxJS streams, WebSockets
Example 1: Observables with Async Pipe
Component:
data$ = this.http.get('/api/products');
Template:
<div *ngIf="data$ | async as products">
{{ products | json }}
</div>
No need to subscribe manually.
Example 2: Loading User Profile
user$ = this.userService.getUser();
<div *ngIf="user$ | async as user">
<p>{{ user.name }}</p>
<p>{{ user.email }}</p>
</div>
Example 3: Using Async Pipe Inside ngFor
orders$ = this.orderService.getOrders();
<div *ngFor="let order of orders$ | async">
{{ order.id }} - {{ order.amount }}
</div>
6. Async Pipe with Multiple Streams
Angular can combine multiple Observables using combineLatest or forkJoin.
result$ = combineLatest([
this.userService.getUser(),
this.orderService.getOrders()
]);
Template
<div *ngIf="result$ | async as data">
{{ data[0].name }}
{{ data[1].length }} orders
</div>
7. Combining Directives and Async Pipe (Real Case Study)
Scenario: You are building an order dashboard.
<div *ngIf="orders$ | async as orders">
<div *ngFor="let order of orders" [ngClass]="{
'delivered': order.status === 'Delivered',
'pending': order.status === 'Pending'
}">
<p>{{ order.amount | currency:'INR' }}</p>
<span [ngStyle]="{
'color': order.status === 'Delivered' ? 'green' : 'orange'
}">
{{ order.status }}
</span>
<div [ngSwitch]="order.paymentMode">
<span *ngSwitchCase="'card'">Card Payment</span>
<span *ngSwitchCase="'cash'">Cash on Delivery</span>
<span *ngSwitchDefault>Other</span>
</div>
</div>
</div>
This combines everything:
Async pipe
ngClass
ngStyle
ngSwitch
Using these together results in clean, scalable, maintainable Angular templates.
8. Best Practices for Directives and Async Pipe
ngClass / ngStyle
Use ngClass for multiple classes
Use ngStyle for dynamic inline styles
Avoid complex expressions inside templates
Keep the logic small and readable
ngSwitch
Async Pipe
Prefer Async Pipe over manual subscription
Avoid calling API repeatedly inside templates
Use local template variables with as value for readability
Works best with Observables, but supports Promises too
Conclusion
Angular Directives and the Async Pipe are essential building blocks for writing clean and scalable user interfaces.
They allow you to:
When combined together, they provide a powerful and elegant way to handle complex UI scenarios without writing extra TypeScript code.