Angular 20 di 2026: Resource API, httpResource, dan Pertanyaan Wawancara
Angular 20 memperkenalkan httpResource dan menstabilkan Resource API untuk pengambilan data berbasis signal. Tutorial praktis yang mencakup resource(), rxResource(), httpResource(), validasi Zod, dan pertanyaan wawancara umum.

Angular 20 menempatkan Resource API dan httpResource di garis depan pengambilan data berbasis signal. API eksperimental ini menggantikan pola subscription HttpClient yang bertele-tele dengan primitif reaktif yang secara otomatis melacak status loading, error, dan resolved.
Resource API mengganti nama request menjadi params dan loader menjadi stream (untuk rxResource). Nilai status kini berupa literal string ('idle', 'loading', 'resolved', 'error', 'reloading', 'local') alih-alih enum numerik. httpResource dibangun di atas HttpClient dan mendukung interceptor serta validasi Zod secara bawaan.
Tiga Varian Resource di Angular 20
Angular 20 menawarkan tiga cara untuk memuat data asinkron sebagai signal. Masing-masing menyasar kasus penggunaan yang berbeda, tetapi semuanya berbagi model reaktif yang sama: mendeklarasikan dependensi, mendefinisikan loader, lalu mengonsumsi hasilnya melalui signal.
resource()bekerja dengan Promise. Ideal saat menggunakanfetch()atau API berbasis promise apa pun.rxResource()bekerja dengan Observable. Pilihan tepat ketika operator RxJS sepertidebounceTime,retry, atauswitchMapdibutuhkan.httpResource()membungkusHttpClientAngular secara langsung. Interceptor, utilitas pengujian, dan validasi schema berfungsi tanpa konfigurasi tambahan.
Perbedaan utama antara httpResource dan dua varian lainnya: httpResource menggunakan HttpClient di baliknya, yang berarti interceptor yang sudah ada tetap berfungsi. API resource() versi awal sepenuhnya melewati HttpClient, sebuah titik masalah besar di Angular 19.
Membangun Profil Pengguna dengan resource()
Fungsi resource() menerima fungsi komputasi params dan fungsi loader. Ketika signal di dalam params berubah, loader dijalankan ulang secara otomatis.
import { Component, signal, resource } from '@angular/core';
interface User {
id: number;
name: string;
email: string;
}
@Component({
selector: 'app-user-profile',
template: `
@if (userResource.hasValue()) {
<h2>{{ userResource.value().name }}</h2>
<p>{{ userResource.value().email }}</p>
} @else if (userResource.isLoading()) {
<p>Loading profile...</p>
} @else if (userResource.error()) {
<p>Failed to load user</p>
}
`,
})
export class UserProfileComponent {
userId = signal(1);
// params produces the reactive dependency
// loader receives it and returns a Promise
userResource = resource<User, number>({
params: () => this.userId(),
loader: async ({ params: id, abortSignal }) => {
const res = await fetch(`/api/users/${id}`, { signal: abortSignal });
return res.json();
},
});
loadUser(id: number) {
this.userId.set(id); // triggers automatic refetch
}
}Parameter abortSignal memungkinkan Angular membatalkan permintaan yang sedang berjalan ketika userId berubah sebelum permintaan sebelumnya selesai. Ini mencegah race condition tanpa pengelolaan subscription secara manual.
Pengambilan Data Reaktif dengan httpResource
httpResource menghilangkan boilerplate dengan menggabungkan deklarasi URL dan eksekusi HTTP dalam satu panggilan. Ia mengembalikan HttpResourceRef yang mengekspos value, isLoading, error, status, dan headers sebagai signal.
import { Component, signal, computed } from '@angular/core';
import { httpResource } from '@angular/common/http';
interface Product {
id: number;
name: string;
price: number;
category: string;
}
@Component({
selector: 'app-product-list',
template: `
@if (products.hasValue()) {
@for (product of products.value(); track product.id) {
<div class="product-card">
<h3>{{ product.name }}</h3>
<span>{{ product.price | currency }}</span>
</div>
}
} @else if (products.isLoading()) {
<p>Loading products...</p>
}
`,
})
export class ProductListComponent {
category = signal('electronics');
// httpResource re-fetches whenever category() changes
products = httpResource<Product[]>(() => ({
url: '/api/products',
params: { category: this.category() },
}));
filterByCategory(cat: string) {
this.category.set(cat); // pending request is cancelled, new one starts
}
}Beberapa detail penting di sini. Fungsi yang diteruskan ke httpResource mengembalikan objek konfigurasi permintaan. Angular melacak pembacaan signal di dalam fungsi tersebut, sehingga mengubah category memicu permintaan GET baru. Jika sebuah permintaan sedang berjalan, Angular membatalkannya sebelum memulai yang baru.
httpResource dirancang untuk pengambilan data (permintaan GET). Menggunakannya untuk operasi POST, PUT, atau DELETE tidak aman karena pembatalan permintaan dapat menggugurkan mutasi di tengah jalan. Untuk operasi tulis, gunakan HttpClient secara langsung atau bungkus mutasi dalam sebuah method service.
Validasi Schema dengan Zod dan httpResource
Respons dari layanan eksternal bisa menyimpang dari bentuk yang diharapkan. Opsi parse pada httpResource mengintegrasikan pustaka validasi schema seperti Zod untuk menangkap ketidaksesuaian saat runtime, alih-alih meneruskan data rusak secara diam-diam.
import { Component, signal } from '@angular/core';
import { httpResource } from '@angular/common/http';
import { z } from 'zod';
// Define the expected shape with Zod
const OrderSchema = z.object({
id: z.number(),
status: z.enum(['pending', 'shipped', 'delivered', 'cancelled']),
total: z.number().positive(),
items: z.array(z.object({
productId: z.number(),
quantity: z.number().int().positive(),
unitPrice: z.number().positive(),
})),
createdAt: z.string().datetime(),
});
type Order = z.infer<typeof OrderSchema>;
@Component({
selector: 'app-order',
template: `
@if (order.hasValue()) {
<h2>Order #{{ order.value().id }}</h2>
<p>Status: {{ order.value().status }}</p>
<p>Total: {{ order.value().total | currency }}</p>
} @else if (order.error()) {
<p>Invalid order data received</p>
}
`,
})
export class OrderComponent {
orderId = signal(42);
// parse validates the response before exposing it as a signal
order = httpResource<Order>(
() => `/api/orders/${this.orderId()}`,
{ parse: OrderSchema.parse }
);
}Ketika API mengembalikan data yang tidak cocok dengan OrderSchema, resource beralih ke status 'error'. Tipe kembalian fungsi parse juga menentukan tipe TypeScript dari value(), sehingga definisi schema menjalankan peran ganda sebagai validator runtime sekaligus penghasil tipe.
rxResource dengan stream dan params di Angular 20
Angular 20 mengganti nama loader menjadi stream dan request menjadi params pada rxResource. Perubahan ini menyelaraskan penamaan dengan semantik streaming yang didukung rxResource. Fungsi stream menerima konteks Observable dan harus mengembalikan sebuah Observable.
import { Component, signal } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';
import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { debounceTime, switchMap } from 'rxjs';
interface SearchResult {
id: number;
title: string;
excerpt: string;
}
@Component({
selector: 'app-search',
template: `
<input (input)="query.set($any($event.target).value)" placeholder="Search..." />
@if (results.isLoading()) {
<p>Searching...</p>
}
@if (results.hasValue()) {
@for (item of results.value(); track item.id) {
<div>{{ item.title }}</div>
}
}
`,
})
export class SearchComponent {
private http = inject(HttpClient);
query = signal('');
// params (was "request") provides the reactive input
// stream (was "loader") returns an Observable
results = rxResource<SearchResult[], string>({
params: () => this.query(),
stream: ({ params: q }) =>
this.http.get<SearchResult[]>('/api/search', {
params: { q },
}),
});
}Berbeda dengan httpResource, rxResource memberikan kendali penuh atas pipeline Observable. Operator seperti debounceTime atau retry dapat dirangkai di dalam stream. Namun, httpResource menangani kasus paling umum (satu permintaan GET) dengan lebih sedikit kode.
Siap menguasai wawancara Angular Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Pelacakan Status: Literal String Menggantikan Enum
Angular 20 mengubah ResourceStatus dari enum numerik menjadi tipe union string. Enam nilai yang mungkin memberikan wawasan rinci tentang siklus hidup resource:
| Status | Arti |
|---|---|
| 'idle' | params mengembalikan undefined — tidak ada permintaan yang dikirim |
| 'loading' | Permintaan pertama sedang berlangsung |
| 'reloading' | Permintaan berikutnya setelah keberhasilan sebelumnya |
| 'resolved' | Data tersedia di value() |
| 'error' | Permintaan gagal — error() berisi error |
| 'local' | Nilai diatur secara lokal melalui .set() atau .update() |
import { Component, signal, resource } from '@angular/core';
@Component({
selector: 'app-status-demo',
template: `
<p>Status: {{ data.status() }}</p>
@switch (data.status()) {
@case ('loading') { <spinner /> }
@case ('reloading') { <subtle-spinner /> }
@case ('resolved') { <data-table [rows]="data.value()" /> }
@case ('error') { <error-banner [error]="data.error()" /> }
@case ('idle') { <p>Select a filter to load data</p> }
}
`,
})
export class StatusDemoComponent {
filter = signal<string | undefined>(undefined);
data = resource({
params: () => this.filter(),
loader: async ({ params: f, abortSignal }) => {
const res = await fetch(`/api/data?filter=${f}`, { signal: abortSignal });
return res.json();
},
});
}Mengembalikan undefined dari params menetapkan status menjadi 'idle' dan mencegah loader dijalankan. Pola ini cocok untuk pengambilan data bersyarat — tampilkan petunjuk hingga pengguna memberikan masukan, lalu muat data.
Sejak Angular 20, memanggil value() pada resource berstatus 'error' melempar exception saat runtime. Selalu lindungi pembacaan dengan hasValue() atau periksa status() sebelum mengakses value(). Ini adalah breaking change dari Angular 19, di mana value() mengembalikan undefined saat error.
Pertanyaan Wawancara httpResource Angular 20
Resource API menjadi topik standar dalam pertanyaan wawancara Angular. Berikut pertanyaan yang menguji pemahaman nyata, melampaui kefasihan permukaan terhadap API.
T: Masalah apa yang diselesaikan httpResource yang tidak diselesaikan resource()?
resource() menggunakan fetch() atau loader berbasis promise mana pun, melewati HttpClient Angular. Ini berarti interceptor (untuk token autentikasi, logging, penanganan error) tidak berlaku. httpResource menggunakan HttpClient secara internal, sehingga interceptor, utilitas pengujian (HttpTestingController), dan fitur seperti withFetch() berfungsi tanpa konfigurasi tambahan.
T: Kapan rxResource sebaiknya dipilih dibanding httpResource?
rxResource memberikan kendali penuh atas Observable melalui fungsi stream-nya. Pilih ia ketika pipeline data membutuhkan operator RxJS — melakukan debounce pada masukan pencarian, mencoba ulang permintaan gagal dengan exponential backoff, atau menggabungkan beberapa stream dengan combineLatest. Untuk permintaan GET sederhana, httpResource membutuhkan lebih sedikit kode.
T: Bagaimana Angular menangani permintaan bersamaan ketika sebuah signal berubah dengan cepat?
Ketiga varian resource membatalkan permintaan yang sedang berjalan ketika params menghasilkan nilai baru. Untuk httpResource dan resource(), AbortSignal membatalkan fetch yang mendasarinya. Untuk rxResource, Angular berhenti berlangganan dari Observable sebelumnya. Ini mencegah respons usang menimpa data yang baru.
T: Apa tujuan status 'local'?
Memanggil .set() atau .update() pada resource mengubah nilainya secara lokal tanpa memicu loader. Status beralih ke 'local', menandakan bahwa nilai saat ini tidak berasal dari server. Ini mendukung pembaruan UI optimistis — UI mencerminkan perubahan seketika sementara permintaan mutasi terpisah berjalan.
T: Bagaimana integrasi Zod bekerja dengan httpResource?
Opsi parse menerima fungsi apa pun dengan signature (data: unknown) => T. Ketika respons HTTP tiba, httpResource meneruskan JSON yang sudah diurai melalui parse sebelum menetapkan value(). Jika parse melempar exception (misalnya ZodError), resource beralih ke status 'error'. Tipe kembalian parse menentukan tipe TypeScript dari value(), memberikan keamanan runtime sekaligus tipe saat kompilasi dari satu definisi schema.
Untuk pendalaman tentang signal Angular dan bagaimana mereka terintegrasi dengan framework secara lebih luas, modul signal mencakup computed signal, effect, dan model reaktivitas.
Migrasi dari Subscription HttpClient ke httpResource
Aplikasi Angular yang sudah ada umumnya mengambil data dengan subscription HttpClient di dalam ngOnInit, atau menggunakan AsyncPipe dengan Observable. Migrasi ke httpResource mengikuti pola yang dapat diprediksi:
// BEFORE: manual subscription in ngOnInit
@Component({ /* ... */ })
export class BeforeComponent implements OnInit, OnDestroy {
private http = inject(HttpClient);
private destroy$ = new Subject<void>();
users: User[] = [];
loading = false;
error: string | null = null;
ngOnInit() {
this.loading = true;
this.http.get<User[]>('/api/users')
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (data) => { this.users = data; this.loading = false; },
error: (err) => { this.error = err.message; this.loading = false; },
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
// AFTER: httpResource handles lifecycle automatically
@Component({ /* ... */ })
export class AfterComponent {
users = httpResource<User[]>(() => '/api/users');
// No ngOnInit, no Subject, no manual unsubscribe
// Template uses users.value(), users.isLoading(), users.error()
}Migrasi ini menghilangkan boilerplate pengelolaan siklus hidup. Pembersihan subscription terjadi otomatis ketika komponen dihancurkan. Status loading dan error sudah terpasang di dalam resource — tidak diperlukan flag boolean terpisah.
Untuk aplikasi yang sudah menggunakan komponen standalone, migrasinya lugas: ganti injeksi HttpClient dan logika subscription dengan satu deklarasi httpResource.
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Kesimpulan
httpResourcemenggantikan subscriptionHttpClientmanual dengan satu deklarasi reaktif yang menangani loading, error, dan pembatalan secara otomatis- Gunakan
resource()untuk API berbasis promise,rxResource()untuk pipeline Observable dengan operator RxJS, danhttpResource()untuk panggilan HTTP standar dengan dukungan interceptor - Angular 20 mengganti nama
requestmenjadiparamsdanloadermenjadistreampadarxResource— perbarui kode yang ada sesuai itu - Nilai status kini berupa literal string (
'idle','loading','resolved','error','reloading','local'), menggantikan enum numerik dari Angular 19 - Opsi
parsepadahttpResourcemengintegrasikan Zod atau Valibot untuk validasi schema runtime yang juga mengarahkan tipe TypeScript - Memanggil
value()pada resource dalam status error melempar exception di Angular 20 — selalu lindungi denganhasValue()atau periksastatus()terlebih dahulu - Semua varian resource membatalkan permintaan yang sedang berjalan secara otomatis ketika dependensi berubah, mencegah race condition tanpa logika pembatalan manual
Tag
Bagikan
Artikel terkait

Angular Standalone Components: Panduan Migrasi dan Praktik Terbaik 2026
Panduan lengkap migrasi Angular standalone components. Langkah-langkah menghapus NgModules, mengaktifkan lazy loading, dan mengadopsi standalone API di Angular 21.

Pertanyaan wawancara Angular 19: Signals, SSR, dan konsep wajib diketahui
Pertanyaan wawancara Angular 19 yang paling umum: Signals, hidrasi inkremental, zoneless change detection, dan API reaktif baru lengkap dengan contoh kode dan jawaban yang diharapkan.

Angular 19 Zoneless: Performa dan Change Detection Tanpa Zone.js
Angular zoneless change detection menghapus Zone.js untuk menghasilkan bundle lebih kecil, rendering lebih cepat, dan reaktivitas eksplisit melalui signals. Panduan mendalam migrasi dari Zone.js ke zoneless Angular, dari API eksperimental di Angular 19 hingga API stabil di Angular 20+.