Route Parameters — Angular

Route parameters memungkinkan URL dinamis. Konfigurasi export const routes: Routes = [ { path: "user/:id", component: UserDetailComponent }, { path…

Route parameter adalah bagian alamat yang isinya berubah-ubah: /menu/1, /menu/2, dan /menu/3 dilayani satu rute menu/:id dan satu komponen, lalu komponennya membaca angka itu dari alamatnya. Bayangin loket pengambilan di warung Martabak Bang Jali. Loketnya cuma satu, tapi nomor di tiketmu yang menentukan martabak mana yang diserahkan. Rute menu/:id itu loketnya, angka di alamat itu nomor tiketnya, dan ?porsi=2 catatan tambahan di tiket yang sama.

🧪 Tebakan cepat: Kamu sedang di /menu/1, halaman Martabak Manis Cokelat Keju. MenuDetail membaca id-nya sekali di ngOnInit dengan this.route.snapshot.paramMap.get('id'). Kamu klik tautan "Lihat juga: Martabak Telur Spesial" (/menu/2). Alamatnya berubah jadi /menu/2. Judul halamannya ikut berubah? Jawabannya ada di bagian "snapshot atau paramMap".

Rute dengan :id

// src/app/app.routes.ts
export const routes: Routes = [
  { path: '', redirectTo: 'menu', pathMatch: 'full' },
  { path: 'menu', component: MenuPage },
  { path: 'menu/:id', component: MenuDetail },
  { path: '**', component: TidakDitemukan },
]
<!-- template MenuPage, dengan RouterLink di imports -->
@for (m of menu; track m.id) {
  <p>
    <a [routerLink]="['/menu', m.id]">{{ m.nama }}</a>
    <a [routerLink]="['/menu', m.id]" [queryParams]="{ porsi: 2 }">2 porsi</a>
  </p>
}
Alamat Rute yang cocok paramMap.get('id')
/menu menu (MenuPage) gak ada
/menu/2 menu/:id (MenuDetail) '2'
/menu/2?porsi=2 menu/:id (MenuDetail) '2'

Membaca id: ActivatedRoute

ActivatedRoute adalah service dari @angular/router yang berisi keterangan rute yang sedang menampilkan komponen ini: parameternya, query-nya, alamatnya.

import { Component, OnInit, inject } from '@angular/core'
import { ActivatedRoute, RouterLink } from '@angular/router'
import { MENU, Menu } from '../menu-data'

@Component({
  selector: 'app-menu-detail',
  standalone: true,
  imports: [RouterLink],
  template: `
    <h2>{{ menu?.nama ?? 'Menu gak ketemu' }}</h2>
    <p>{{ porsi }} porsi, total Rp{{ (menu?.harga ?? 0) * porsi }}</p>
    <p>Lihat juga:
      @for (m of lainnya; track m.id) {
        <a [routerLink]="['/menu', m.id]">{{ m.nama }}</a>
      }
    </p>
  `,
})
export class MenuDetail implements OnInit {
  private route = inject(ActivatedRoute)
  menu?: Menu
  porsi = 1

  get lainnya() {
    return MENU.filter((m) => m.id !== this.menu?.id)
  }

  ngOnInit() {
    const id = this.route.snapshot.paramMap.get('id') // '2', teks
    this.menu = MENU.find((m) => m.id === Number(id))
    this.porsi = Number(this.route.snapshot.queryParamMap.get('porsi') ?? 1)
  }
}

snapshot atau paramMap

Router memakai ulang komponen yang sama kalau yang berubah cuma parameternya. Dari /menu/1 ke /menu/2, MenuDetail gak dibuat lagi, jadi ngOnInit gak jalan lagi:

Langkah Alamat Instance MenuDetail Dibaca di ngOnInit Yang tampil
Buka dari Menu /menu/1 #1 dibuat '1' Martabak Manis Cokelat Keju
Klik "Lihat juga: Martabak Telur Spesial" /menu/2 #1 dipakai ulang gak dibaca lagi Martabak Manis Cokelat Keju
Kembali ke Menu, buka lagi /menu/2 #2 dibuat '2' Martabak Telur Spesial

Jadi judulnya gak berubah: alamatnya /menu/2, tapi halamannya masih Martabak Manis Cokelat Keju. Itu jawaban tebakannya. route.snapshot sendiri sudah berisi id baru; yang basi adalah salinan yang kamu ambil sekali di ngOnInit.

Kalau halamannya punya tautan ke id lain, dengarkan perubahannya dengan paramMap, sebuah Observable:

menu = signal<Menu | undefined>(undefined)
porsi = signal(1)

ngOnInit() {
  this.route.paramMap.subscribe((params) => {
    this.menu.set(MENU.find((m) => m.id === Number(params.get('id'))))
  })
  this.route.queryParamMap.subscribe((query) => {
    this.porsi.set(Number(query.get('porsi') ?? 1))
  })
}

Template-nya membaca menu()?.nama dan porsi(). Tiap kali parameternya berubah, fungsinya jalan lagi, signal-nya berganti, dan halamannya ikut berganti. Kenapa signal: sejak Angular 22 komponen secara bawaan OnPush, jadi properti biasa yang diganti dari dalam subscribe gak memberi kabar, dan judulnya tetap menu lama (dicoba di editor latihan, Angular 22). Langganan ke Observable milik ActivatedRoute gak perlu kamu unsubscribe sendiri: router membereskannya saat komponennya dihancurkan.

Query parameter: ?porsi=2

Path parameter (/menu/2) menunjuk barang yang mana, dan rutenya gak cocok tanpa dia. Query parameter (?porsi=2, ?urut=harga) cuma keterangan tambahan yang boleh ada boleh enggak.

Latihan: id dari editor

Editor latihan gak punya daftar rute, jadi editor memberi rute aktif palsu dengan id 9f42c1. Komponen UserComponent di latihan cukup meminta ActivatedRoute, membaca paramMap.get('id') di ngOnInit (lewat snapshot atau subscribe), lalu menampilkannya: <p>User ID: {{ userId }}</p> jadi User ID: 9f42c1.

💡 id sebagai @Input

Di proyek Angular CLI, provideRouter(routes, withComponentInputBinding()) mengisi @Input() id milik komponen rute langsung dari alamatnya, termasuk saat parameternya berubah. Editor latihan dan Playground memasang router-nya sendiri tanpa fitur itu, jadi di sini kita memakai ActivatedRoute.

⚠️ Jebakan yang sering ketemu

TL;DR: Rute menu/:id melayani /menu/1, /menu/2, dan seterusnya dengan satu komponen. Komponen itu membaca parameternya lewat inject(ActivatedRoute): snapshot.paramMap.get('id') sekali di ngOnInit, atau paramMap.subscribe(...) kalau halamannya bisa pindah ke id lain, karena router memakai ulang komponen yang sama dan ngOnInit gak jalan lagi. Query parameter ?porsi=2 dikirim dengan [queryParams] dan dibaca dengan queryParamMap.get('porsi'). Semua nilai dari alamat berupa teks atau null, jadi Number(...) dulu sebelum dihitung.

Yang akan kamu pelajari