Guards dan Resolvers — Angular

Guards mengontrol akses ke route, Resolvers menyiapkan data sebelum navigasi. Functional Guard export const authGuard: CanActivateFn = (route, state) => {…

Guard adalah fungsi yang ditanya router sebelum masuk ke sebuah rute (atau sebelum keluar darinya): "boleh lewat?". Resolver adalah fungsi yang menyiapkan data sebuah rute sebelum halamannya tampil. Bayangin warung Martabak Bang Jali punya dapur di belakang. Di pintunya ada penjaga yang memeriksa kartu karyawan sebelum kamu masuk (canActivate), dan menahanmu sebelum keluar kalau pesanan di tanganmu belum dikirim (canDeactivate). Sementara itu pelayan menyiapkan papan menu di meja sebelum kamu duduk (resolve).

🧪 Tebakan cepat: authGuard dibuat mengembalikan false (bukan router.parseUrl('/login')) saat pembeli belum masuk. Pembeli yang belum masuk mengklik Pesanan. Apa yang tampil? Jawabannya ada di bagian "true, false, atau UrlTree".

CanActivateFn: penjaga sebelum masuk

// src/app/auth.service.ts
@Injectable({ providedIn: 'root' })
export class AuthService {
  masuk = signal(false)
}

// src/app/auth.guard.ts
import { inject } from '@angular/core'
import { CanActivateFn, Router } from '@angular/router'
import { AuthService } from './auth.service'

export const authGuard: CanActivateFn = (route, state) => {
  const auth = inject(AuthService)
  const router = inject(Router)
  console.log('authGuard jalan, menuju ' + state.url)

  if (auth.masuk()) return true
  return router.parseUrl('/login')
}
// src/app/app.routes.ts
export const routes: Routes = [
  { path: '', redirectTo: 'menu', pathMatch: 'full' },
  { path: 'menu', component: MenuPage },
  { path: 'pesanan', component: PesananPage, canActivate: [authGuard] },
  { path: 'login', component: LoginPage },
]

true, false, atau UrlTree

Guard mengembalikan Yang terjadi
true navigasi lanjut, PesananPage tampil
false navigasi batal: alamat dan halaman tetap, tanpa pesan, tanpa error
router.parseUrl('/login') sebuah UrlTree: router membatalkan /pesanan dan pindah ke /login

Jadi dengan return false, klik Pesanan gak menghasilkan apa-apa: pembeli tetap di Menu dan gak tahu kenapa. Itu jawaban tebakannya. Kembalikan UrlTree supaya dia diarahkan ke halaman masuk.

Ini log di Playground saat pembeli yang belum masuk mengklik Pesanan, lalu Masuk:

Kejadian Console Halaman
Klik Pesanan authGuard jalan, menuju /pesanan Masuk dulu (/login)
Klik Masuk: masuk.set(true), lalu router.navigateByUrl('/pesanan') authGuard jalan, menuju /pesanan Pesanan

CanDeactivateFn: penjaga sebelum keluar

// src/app/belum-kirim.guard.ts
export const belumKirimGuard: CanDeactivateFn<PesananPage> = (page) => {
  console.log('belumKirimGuard jalan, catatan: ' + JSON.stringify(page.catatan))
  return page.catatan === '' || confirm('Pesanan belum dikirim. Tetap pindah?')
}

// di daftar rute
{ path: 'pesanan', component: PesananPage, canActivate: [authGuard], canDeactivate: [belumKirimGuard] },

ResolveFn: data siap sebelum halaman tampil

// src/app/menu.resolver.ts
export const menuResolver: ResolveFn<Menu[]> = () => {
  console.log('menuResolver jalan')
  return inject(MenuApi).ambilMenu()
}

// di daftar rute
{ path: 'menu', component: MenuPage, resolve: { menu: menuResolver } },
// src/app/menu/menu.ts
export class MenuPage {
  menu: Menu[] = inject(ActivatedRoute).snapshot.data['menu']

  constructor() {
    console.log('MenuPage dibuat, menu: ' + this.menu.length)
  }
}
Waktu Console Yang tampil
Klik Menu dari Pesanan menuResolver jalan masih Pesanan
600 ms kemudian, data sampai MenuPage dibuat, menu: 3 Menu dengan 3 martabak

📌 Kapan resolver, kapan di komponen

Resolver membuat pembeli menunggu tanpa tanda apa pun. Untuk data yang cepat dan wajib ada (detail satu menu), resolver pas. Untuk data yang lambat, ambil di komponen seperti di pelajaran HttpClient, lalu tampilkan "Memuat...". Bentuk lama guard berupa class dengan implements CanActivate masih ketemu di proyek lama; bentuk fungsi di atas yang dipakai sekarang.

⚠️ Jebakan yang sering ketemu

TL;DR: Guard menjawab "boleh lewat?" untuk sebuah rute. CanActivateFn ditanya sebelum masuk (canActivate: [authGuard]), mengambil service dengan inject(), dan mengembalikan true untuk lanjut, false untuk membatalkan diam-diam, atau UrlTree seperti router.parseUrl('/login') untuk mengalihkan. CanDeactivateFn<PesananPage> ditanya sebelum keluar, menerima komponennya, dan cocok untuk form yang belum dikirim. ResolveFn di resolve: { menu: menuResolver } memuat data sebelum navigasi selesai, jadi komponen dibuat dengan data yang sudah siap dan halaman lama bertahan selama menunggu.

Yang akan kamu pelajari