Reactive Forms, HttpClient, dan HTTP Interceptor
Dari form edit Product, lewat request HTTP, sampai satu tempat untuk header dan error global
HttpClient, dan apa yang Anda taruh di HTTP Interceptor?"
dalam 60 detik.
1. Reactive vs template-driven forms
Angular punya dua pendekatan form. Keduanya memakai kelas dasar yang sama:
FormControl "Tracks the value and validation status of an individual form control", dan
FormGroup melakukan hal yang sama "for a collection of form controls"
[1].
Bedanya ada di siapa yang membuat object itu:
| Reactive | Template-driven | Padanan React | |
|---|---|---|---|
| Form model | "Explicit, created in component class" | "Implicit, created by directives" (ngModel) | controlled input + state / React Hook Form |
| Data model | "Structured and immutable" | "Unstructured and mutable" | – |
| Data flow | "Synchronous" | "Asynchronous" | – |
| Validasi | "Functions" | "Directives" | fungsi validasi / schema Zod |
Sumber tabel: Angular v16: Key differences [1].
Alasan memilih yang harus Anda ucapkan: Reactive Forms "are more robust: they're more scalable, reusable, and testable", sedangkan template-driven "are useful for adding a simple form to an app, such as an email list signup form" [1]. Reactive Forms juga "can be tested without rendering the UI", sedangkan test template-driven "requires a detailed knowledge of the change detection process" [1].
// product-form.component.ts (AppModule meng-import ReactiveFormsModule)
export class ProductFormComponent {
form = this.fb.group({
name: ['', Validators.required],
price: [0, [Validators.required, Validators.min(0.01)]], // validator = fungsi
});
constructor(private fb: FormBuilder, private products: ProductService) {}
save() {
if (this.form.invalid) return;
this.products.create(this.form.getRawValue()).subscribe();
}
}
<form [formGroup]="form" (ngSubmit)="save()">
<input formControlName="name">
<input formControlName="price" type="number">
<button type="submit" [disabled]="form.invalid">Simpan</button>
</form>
<!-- template-driven (FormsModule): model implisit, two-way binding -->
<input name="name" [(ngModel)]="product.name" required>
FormBuilder hanya pembuat singkat: "factory methods for generating instances" control(), group(), dan array().
Setiap control punya valueChanges, Observable yang bisa Anda pipe dengan operator dari lesson 0015
[2].
2. HttpClient: semuanya Observable
"The HttpClient service makes use of observables for all transactions"
[3].
Di aplikasi NgModule, HttpClientModule di-import di AppModule, lalu HttpClient di-inject ke Angular Service.
Tiga hal yang membedakannya dari fetch/Axios:
- Lazy. "the method doesn't begin its HTTP request until you call
subscribe()" [4]. Pakaiasyncpipe di template. - Generic hanya untuk compiler.
get<Product[]>()adalah type assertion: "This is a build-time check and doesn't guarantee that the server actually responds with an object of this type." Karena itu pakaiinterface, bukan class [4]. - Error =
HttpErrorResponse. Error dari server (404, 500) dan error jaringan sama-sama ditangkap diHttpErrorResponse; error jaringan "havestatusset to0" [5].
@Injectable({ providedIn: 'root' })
export class ProductService {
constructor(private http: HttpClient) {}
getAll(): Observable<Product[]> {
return this.http.get<Product[]>('/api/products').pipe(
retry(2), // subscribe ulang = request ulang
catchError((e: HttpErrorResponse) => throwError(() => new Error(`Gagal (${e.status})`)))
);
}
create(dto: CreateProductDto) { return this.http.post<Product>('/api/products', dto); }
}
3. HTTP Interceptor: satu tempat untuk setiap request
Interceptor "inspect and transform HTTP requests from your application to a server" dan juga response-nya.
Tanpa interceptor, tugas seperti autentikasi dan logging harus ditulis "explicitly for each HttpClient method call"
[6].
Dokumentasi Angular sendiri menyebutnya "a common middleware pattern found in frameworks such as Express.js"
[6].
Padanan React yang paling dekat: axios.interceptors.request.use(...) [7].
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private auth: AuthService, private router: Router) {}
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const authReq = req.clone({ setHeaders: { Authorization: `Bearer ${this.auth.token}` } });
return next.handle(authReq).pipe( // Observable response
catchError((e: HttpErrorResponse) => {
if (e.status === 401) this.router.navigate(['/login']); // error global
return throwError(() => e);
})
);
}
}
// app.module.ts
providers: [{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }]
Empat aturan yang sering ditanyakan:
- Clone, jangan ubah. Property
HttpRequestitureadonlykarena request bisa di-retry: "Immutability ensures that interceptors see the same request for each try." Pola menambah header lewatclone"is so common that there's asetHeadersshortcut for it" [8]. next.handle(). "The finalnextin the chain is theHttpClientbackend handler". Interceptor boleh tidak memanggilnya (mis. cache) [6]. Padanan Express:next().multi: true. MembuatHTTP_INTERCEPTORS"a token for a multiprovider that injects an array of values"; interceptor harus disediakan di injector yang sama denganHttpClientModule, biasanyaAppModule[6].- Urutan. "Angular applies interceptors in the order that you provide them." Request Auth → Logging, response Logging → Auth [6].
Angular 16 juga punya interceptor berbentuk fungsi (HttpInterceptorFn, didaftarkan lewat provideHttpClient(withInterceptors([...]))), yang
"are executed in an injection context" sehingga bisa memakai inject()
[9].
Itu pola aplikasi standalone; di proyek NgModule jawab dengan class + HTTP_INTERCEPTORS dulu.
Detail JWT, CORS, dan error handling di API dibahas di lesson 0017.
Latihan 1 — Reactive atau template-driven?
Jawab tanpa scroll ke atas; kalau ragu, tebak dulu, lalu baca penjelasannya.
Kuis di lesson ini adalah feedback cepat, bukan bukti penguasaan; yang dicatat hanya hasil di mock interview 0018.
Latihan 2 — HttpClient dan interceptor
Sebelum mengklik, ucapkan alasannya dalam satu kalimat.
Latihan 3 — Ucapkan dengan suara keras
Pasang timer 60 detik. Jawab dengan suara keras, seolah sedang interview. Jangan buka jawaban contoh sebelum selesai.
"Kapan Anda memilih Reactive Forms dibanding template-driven, apa yang dikembalikan HttpClient, dan apa yang Anda taruh di HTTP Interceptor?"
Bandingkan dengan jawaban contoh dan checklist-nya
"Reactive Forms membuat form model secara eksplisit di class component dengan FormGroup dan FormControl;
template-driven membuatnya implisit lewat ngModel. Untuk form besar dengan validasi dan field dinamis saya pilih Reactive:
alurnya sinkron, validatornya fungsi, dan bisa di-unit test tanpa render. Template-driven cukup untuk form kecil.
HttpClient mengembalikan Observable yang baru mengirim request saat di-subscribe; generic get<Product[]>
hanya type assertion saat compile. Error datang sebagai HttpErrorResponse dan saya tangani dengan catchError.
Interceptor adalah class yang mengimplementasikan HttpInterceptor: karena request immutable, saya clone
lalu setHeaders Authorization, kemudian next.handle. Saya daftarkan dengan HTTP_INTERCEPTORS
dan multi: true; urutannya mengikuti urutan provider. Di sana juga saya tangani 401 secara global.
Padanannya interceptor Axios, atau middleware Express di sisi backend."
Checklist: apakah Anda menyebut…
- Reactive: model eksplisit di class (
FormGroup/FormControl); template-driven: implisit lewatngModel? - alasan memilih: Reactive scalable, sinkron, validator berupa fungsi, mudah diuji; template-driven untuk form sederhana?
HttpClientmengembalikan Observable yang baru request saatsubscribe?- generic
get<T>hanya type assertion compile-time; error =HttpErrorResponse, ditanganicatchError? - interceptor:
clonerequest (immutable) +setHeadersAuthorization, lalunext.handle? - didaftarkan lewat
HTTP_INTERCEPTORSmulti: true, urutan = urutan provider; dipakai untuk error global (401), padanan interceptor Axios?
Kalau kurang dari 4 poin, ulangi sebelum lesson 0017. Soal ini juga akan muncul lagi di mock interview 0018.
Ringkasan untuk dicetak: Reference: Forms, HttpClient, dan HTTP Interceptor · Istilah: GLOSSARY.md
Ada yang belum jelas? Tanyakan ke saya (agent) di terminal, misalnya: "bagaimana menulis unit test untuk interceptor dengan HttpTestingController?" atau "kapan saya pakai patchValue dan bukan setValue?"