Learn Angular - Directives & Pipes
Episode 5 of 24

Learn Angular - Directives & Pipes

This episode covers directives and pipes: built-in directives such as *ngIf, *ngFor, ngClass, and ngStyle along with the modern @if and @for control flow, how to create custom directives and custom pipes, and using built-in pipes to format display data.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Components give structure, but most of the "life" in a template comes from directives and pipes. Directives change the appearance or behavior of elements, while pipes format data before it's displayed.

Episode 5 covers built-in directives such as *ngIf, *ngFor, ngClass, and ngStyle, the modern @if and @for control flow, creating custom directives and custom pipes, and the built-in pipes you'll use most often. After this, your templates will be far more expressive.

Built-in Directives and Modern Control Flow

*ngIf and *ngFor

Classically, conditionals and loops use structural directives with an asterisk:

HTMLClassic ngIf and ngFor directives
<li *ngFor="let produk of daftarProduk; trackBy: trackById">
  {{ produk.nama }}
</li>
<div *ngIf="sedangMuat; else kosong">Memuat data...</div>
<ng-template #kosong>Belum ada data</ng-template>

*ngFor loops over a list, while *ngIf displays an element only when its condition is true. trackBy helps Angular identify the same items so re-rendering is more efficient.

Modern Control Flow: @if and @for

Since Angular 17, there's a new control flow syntax that's more concise, performs better, and doesn't require imports:

HTMLModern control flow
@for (produk of daftarProduk; track produk.id) {
  <p>{{ produk.nama }} - Rp {{ produk.harga }}</p>
} @empty {
  <p>Keranjang masih kosong</p>
}
 
@if (sedangMuat) {
  <p>Memuat data...</p>
} @else {
  <button (click)="muatData()">Muat Data</button>
}

@for ... track produk.id replaces *ngFor with trackBy, and the @empty block handles the empty-list case without an extra <ng-template>. This is the recommended way for all new projects.

Structural and Attribute Directives

The Difference Between Them

  • Structural directive changes the DOM structure, for example *ngIf and *ngFor. Marked with an asterisk.
  • Attribute directive changes the appearance or behavior of an existing element, for example ngClass and ngStyle.
HTMLngClass and ngStyle
<div [ngClass]="{ aktif: terpilih, error: gagal }">Status</div>
<div [ngStyle]="{ backgroundColor: terpilih ? 'green' : 'gray' }">Warna</div>

[ngClass] accepts a class object with conditions, while [ngStyle] binds style values directly. Both remain useful even now that modern control flow is available.

Creating a Custom Directive

A directive is created with @Directive. Here's an attribute directive that adds a highlight when an element is hovered:

JSCustom highlight directive
import { Directive, ElementRef, HostListener, input } from '@angular/core';
 
@Directive({
  selector: '[appHighlight]',
  standalone: true,
})
export class HighlightDirective {
  readonly appHighlight = input<string>('yellow');
 
  constructor(private el: ElementRef) {}
 
  @HostListener('mouseenter') onMouseEnter(): void {
    this.el.nativeElement.style.backgroundColor = this.appHighlight();
  }
 
  @HostListener('mouseleave') onMouseLeave(): void {
    this.el.nativeElement.style.backgroundColor = '';
  }
}

The selector [appHighlight] means the directive is used as an attribute: <p appHighlight="lightblue">teks</p>. @HostListener listens for events from the host element. A standalone directive can be used directly without an NgModule.

Built-in Pipes and Custom Pipes

Built-in Pipes

Pipes format data in templates with the | operator. The built-in pipes you'll use most often:

HTMLBuilt-in pipes
<p>{{ harga | currency: 'IDR' }}</p>
<p>{{ dibuatPada | date: 'medium' }}</p>
<p>{{ nama | uppercase }}</p>
<p>{{ persen | percent }}</p>

currency, date, uppercase, and percent are built-in pipes. The async pipe is also essential — it subscribes to an Observable or signal automatically and cleans up the subscription when the component is destroyed.

Creating a Custom Pipe

For custom formatting, create your own pipe with @Pipe:

JSCustom rupiah pipe
import { Pipe, PipeTransform } from '@angular/core';
 
@Pipe({
  name: 'rupiah',
  standalone: true,
})
export class RupiahPipe implements PipeTransform {
  transform(nilai: number): string {
    return nilai.toLocaleString('id-ID', {
      style: 'currency',
      currency: 'IDR',
      maximumFractionDigits: 0,
    });
  }
}

The pipe above is used with {{ harga | rupiah }}. A pipe class must implement PipeTransform with the transform method. Pure pipes (the default) only run again when their input changes, which makes them efficient.

Wrap Up

Key takeaways:

  • *ngIf and *ngFor are classic structural directives; @if and @for are their modern replacements.
  • ngClass and ngStyle change classes and styles based on conditions.
  • Attribute directives modify an element; structural directives change the DOM structure.
  • Custom directives are created with @Directive and @HostListener.
  • Built-in pipes format currency, date, percent, and more.
  • Custom pipes implement PipeTransform and are used with the | operator.

In the next episode, episode 6, we'll cover services and dependency injection — creating services and injecting them into components, provider scope at the root, module, and component level, singleton services with hierarchical injectors, and interceptors and provider configuration.

Learn Angular - Directives & Pipes | Learn Angular