Sintaks Control Flow Angular di 2026: @if, @for, @switch dan Pertanyaan Wawancara

Pelajari sintaks control flow Angular (@if, @for, @switch) yang menggantikan directive struktural. Panduan lengkap dengan contoh kode, tips migrasi, dan pertanyaan wawancara teknis.

Angular control flow syntax @if @for @switch guide

Sintaks control flow Angular (@if, @for, @switch) menggantikan directive struktural *ngIf, *ngFor, dan *ngSwitch yang telah mendominasi template Angular selama bertahun-tahun. Diperkenalkan di Angular 17, stabil sejak Angular 18, dan menjadi default di Angular 22, sintaks berbasis blok ini membawa template lebih dekat ke konstruksi pemrograman standar sekaligus memungkinkan optimasi performa yang lebih baik.

Migrasi tersedia

Jalankan ng generate @angular/core:control-flow untuk melakukan migrasi otomatis dari directive struktural ke sintaks control flow baru. Kedua sintaks dapat hidup berdampingan selama migrasi, namun ngIf, ngFor, dan ngSwitch sudah deprecated sejak Angular 19.

Mengapa Angular Mengganti Directive Struktural dengan Sintaks Blok

Directive struktural memerlukan import CommonModule, menggunakan mikrosintaks yang berbeda dari JavaScript, dan memaksa developer untuk membungkus konten dalam ng-template untuk kondisi yang kompleks. Sintaks control flow baru menyelesaikan masalah-masalah ini:

  • Tidak perlu import: @if, @for, dan @switch sudah built-in di framework
  • Sintaks mirip JavaScript: kondisi dan loop terbaca seperti kode standar
  • Dukungan native @else: tidak perlu lagi referensi ng-template untuk branch else
  • Tree-shaking lebih baik: blok control flow yang tidak digunakan tidak berdampak pada ukuran bundle

Dokumentasi Angular merekomendasikan untuk mengadopsi sepenuhnya sintaks blok di semua proyek baru, dan melakukan migrasi codebase yang ada secara bertahap.

@if: Conditional Rendering Tanpa ng-template

Blok @if merender konten secara kondisional berdasarkan ekspresi yang truthy. Berbeda dengan *ngIf, blok ini mendukung branch @else if dan @else secara langsung:

user-status.component.tstypescript
import { Component, input } from '@angular/core';

@Component({
  selector: 'app-user-status',
  template: `
    @if (user().role === 'admin') {
      <app-admin-panel />
    } @else if (user().role === 'editor') {
      <app-editor-panel />
    } @else {
      <app-viewer-panel />
    }
  `
})
export class UserStatusComponent {
  user = input.required<{ role: string }>();
}

Blok @if juga mendukung aliasing variabel dengan keyword as, yang mengekstrak nilai dari ekspresi bersarang:

html
<!-- Menghindari akses properti berulang -->
@if (user().profile?.settings?.theme; as theme) {
  <p>Tema saat ini: {{ theme }}</p>
}

Pola ini sangat berguna ketika bekerja dengan data async atau objek yang deeply nested, karena variabel yang di-alias hanya terdefinisi ketika kondisi bernilai truthy.

@for: Iterasi dengan Track Expression yang Wajib

Blok @for melakukan iterasi pada iterable JavaScript apa pun, dengan optimasi untuk array. Berbeda dengan *ngFor, blok ini memerlukan ekspresi track:

product-list.component.tstypescript
import { Component, input } from '@angular/core';

interface Product {
  id: string;
  name: string;
  price: number;
}

@Component({
  selector: 'app-product-list',
  template: `
    @for (product of products(); track product.id) {
      <app-product-card [product]="product" />
    } @empty {
      <p>Tidak ada produk tersedia</p>
    }
  `
})
export class ProductListComponent {
  products = input.required<Product[]>();
}

Ekspresi track memberitahu Angular cara mengidentifikasi setiap item di setiap re-render. Memilih properti tracking yang tepat berdampak langsung pada performa:

Ekspresi TrackKasus PenggunaanDampak Performa
track item.idItem dengan identifier unikOptimal: update DOM minimal
track $indexDaftar statis yang tidak pernah diurutkan ulangDapat diterima: re-render penuh saat pengurutan ulang
track itemTracking referensiBuruk: referensi baru = node DOM baru

Variabel Kontekstual dalam Blok @for

Angular menyediakan variabel implisit di dalam blok @for yang mengekspos metadata iterasi:

html
@for (item of items(); track item.id; let i = $index, isLast = $last) {
  <li class="item" [class.last]="isLast">
    {{ i + 1 }}. {{ item.name }}
  </li>
}

Variabel kontekstual yang tersedia:

  • $index: posisi berbasis nol
  • $count: jumlah total item
  • $first, $last: flag boolean untuk item pertama/terakhir
  • $even, $odd: flag boolean berdasarkan paritas index

Siap menguasai wawancara Angular Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

@switch: Conditional Branching yang Type-Safe

Blok @switch menyediakan pattern matching yang exhaustive dengan perbandingan strict equality (===):

status-badge.component.tstypescript
import { Component, input } from '@angular/core';

type OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered';

@Component({
  selector: 'app-status-badge',
  template: `
    @switch (status()) {
      @case ('pending') {
        <span class="badge badge-gray">Menunggu</span>
      }
      @case ('processing') {
        <span class="badge badge-blue">Diproses</span>
      }
      @case ('shipped') {
        <span class="badge badge-yellow">Dikirim</span>
      }
      @case ('delivered') {
        <span class="badge badge-green">Terkirim</span>
      }
    }
  `
})
export class StatusBadgeComponent {
  status = input.required<OrderStatus>();
}

Berbeda dengan switch JavaScript, @switch Angular tidak memiliki perilaku fallthrough. Beberapa kondisi yang menargetkan blok yang sama memerlukan statement @case berurutan:

html
@switch (userRole()) {
  @case ('admin')
  @case ('superadmin') {
    <app-admin-dashboard />
  }
  @case ('editor')
  @case ('reviewer') {
    <app-editor-dashboard />
  }
  @default {
    <app-viewer-dashboard />
  }
}

Pengecekan Tipe Exhaustive dengan @default never

Angular 22 mendukung pengecekan exhaustiveness pada waktu kompilasi. Menggunakan @default never; mendeklarasikan bahwa tidak ada kasus tersisa yang seharusnya ada:

typescript
type Theme = 'light' | 'dark' | 'system';

// Error kompilasi jika ada nilai Theme yang hilang dari blok @case
@switch (theme()) {
  @case ('light') { /* ... */ }
  @case ('dark') { /* ... */ }
  @case ('system') { /* ... */ }
  @default never;
}

Menambahkan nilai baru ke union Theme akan memicu error kompilasi, memaksa developer untuk menangani semua kasus.

Sintaks Control Flow dan Integrasi Angular Signals

Blok control flow terintegrasi dengan mulus dengan Angular Signals, memungkinkan reaktivitas yang fine-grained:

dashboard.component.tstypescript
import { Component, computed, signal } from '@angular/core';

@Component({
  selector: 'app-dashboard',
  template: `
    @if (isLoading()) {
      <app-skeleton />
    } @else if (hasError()) {
      <app-error [message]="errorMessage()" />
    } @else {
      @for (item of filteredItems(); track item.id) {
        <app-item-card [item]="item" />
      } @empty {
        <p>Tidak ada item yang cocok dengan filter</p>
      }
    }
  `
})
export class DashboardComponent {
  items = signal<Item[]>([]);
  filter = signal('');
  isLoading = signal(true);
  hasError = signal(false);
  errorMessage = signal('');

  // Computed signal hanya menghitung ulang ketika dependencies berubah
  filteredItems = computed(() =>
    this.items().filter(item =>
      item.name.toLowerCase().includes(this.filter().toLowerCase())
    )
  );
}

Ketika signal diperbarui, Angular hanya mengevaluasi ulang blok control flow yang terpengaruh. Dikombinasikan dengan deteksi perubahan zoneless Angular, ini memungkinkan rendering yang sangat teroptimasi.

Pertanyaan Wawancara Umum tentang Control Flow Angular

Wawancara teknis sering menguji pemahaman tentang sintaks control flow, terutama perbedaan dari directive struktural.

Pertanyaan 1: Mengapa track wajib di @for?

Ekspresi track menyediakan Angular dengan identitas yang stabil untuk setiap item. Tanpa itu, Angular tidak dapat secara efisien menentukan node DOM mana yang harus dibuat, diperbarui, atau dihancurkan ketika koleksi berubah. Mewajibkan track memaksa developer untuk membuat keputusan eksplisit tentang identitas, menghindari jebakan performa dari tracking referensi implisit.

Pertanyaan 2: Bagaimana @if berbeda dari *ngIf dengan async pipe?

Keduanya dapat menangani observable, tetapi @if dengan signals menyediakan akses sinkron ke nilai:

typescript
// Dengan *ngIf dan async pipe (legacy)
<div *ngIf="user$ | async as user">{{ user.name }}</div>

// Dengan @if dan signals (modern)
@if (user(); as user) {
  <div>{{ user.name }}</div>
}

Pendekatan signal menghindari manajemen subscription dan terintegrasi lebih baik dengan deteksi perubahan Angular.

Pertanyaan 3: Bisakah @switch menggantikan rantai @if/@else if yang kompleks?

@switch sebaiknya digunakan ketika membandingkan ekspresi tunggal dengan beberapa nilai diskret. Untuk kondisi boolean kompleks yang melibatkan ekspresi berbeda, @if/@else if tetap lebih sesuai:

html
<!-- Gunakan @switch untuk ekspresi tunggal, banyak nilai -->
@switch (status()) {
  @case ('active') { ... }
  @case ('inactive') { ... }
}

<!-- Gunakan @if untuk banyak ekspresi -->
@if (isAdmin() && hasPermission('write')) {
  ...
} @else if (isEditor()) {
  ...
}

Pertanyaan 4: Apa yang terjadi pada @for ketika koleksi kosong?

Ketika iterable tidak berisi item, Angular melewatkan konten @for sepenuhnya. Blok @empty opsional merender konten fallback dalam kasus ini. Tanpa @empty, tidak ada yang dirender, yang berbeda dari beberapa framework yang merender container kosong.

Migrasi dari Directive Struktural ke Control Flow

Angular CLI menyediakan skematik migrasi otomatis:

bash
# Migrasi seluruh proyek
ng generate @angular/core:control-flow

# Migrasi direktori tertentu
ng generate @angular/core:control-flow --path=src/app/features

Skematik menangani sebagian besar transformasi secara otomatis, tetapi tinjau output untuk kasus khusus:

Directive StrukturalEquivalent Control Flow
*ngIf="condition"@if (condition) { }
*ngIf="condition; else elseBlock"@if (condition) { } @else { }
*ngFor="let item of items"@for (item of items; track item) { }
*ngFor="let item of items; index as i"@for (item of items; track item; let i = $index) { }
[ngSwitch] + *ngSwitchCase@switch + @case

Setelah migrasi, hapus import CommonModule dari standalone components yang tidak lagi menggunakan directive struktural.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Poin Penting untuk Sintaks Control Flow Angular

  • Sintaks control flow Angular (@if, @for, @switch) adalah pendekatan yang direkomendasikan sejak Angular 19, menggantikan directive struktural
  • Ekspresi track di @for wajib: gunakan identifier unik seperti item.id untuk performa optimal, hindari track item yang menyebabkan re-render berbasis referensi
  • @if mendukung branch native @else if dan @else tanpa ng-template, dan keyword as membuat alias nilai truthy untuk penggunaan ulang
  • @switch menggunakan strict equality dan tidak memiliki fallthrough: statement @case berurutan menargetkan blok yang sama, dan @default never; memungkinkan pengecekan tipe exhaustive
  • Blok control flow terintegrasi dengan Signals untuk reaktivitas fine-grained: hanya blok yang terpengaruh yang di-render ulang ketika signals diperbarui
  • Jalankan ng generate @angular/core:control-flow untuk melakukan migrasi template yang ada secara otomatis
Tantangan harian

Bisakah kamu menemukan bug di Angular?

Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Anthony Fillion-Maillet

Ditulis oleh

Anthony Fillion-Maillet

Pendiri SharpSkill

Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.

Diperbarui 3 September 2026

Tag

#angular
#control-flow
#template-syntax
#interview

Bagikan

Artikel terkait