Angular 20 nel 2026: Resource API, httpResource e domande da colloquio
Angular 20 introduce la Resource API e httpResource come alternative signal-based alle sottoscrizioni manuali di HttpClient. Questa guida copre tutte e tre le varianti Resource, la validazione con Zod e le domande più frequenti nei colloqui tecnici.

Angular 20 porta la Resource API e httpResource al centro del data fetching basato su signal. Queste API sostituiscono i pattern verbosi delle sottoscrizioni HttpClient con primitive reattive che tracciano automaticamente gli stati di caricamento, errore e risoluzione.
La Resource API rinomina request in params e loader in stream (per rxResource). I valori di stato sono ora stringhe letterali ('idle', 'loading', 'resolved', 'error', 'reloading', 'local') invece di enum numerici. httpResource si basa su HttpClient, supportando interceptor e validazione Zod senza configurazioni aggiuntive.
Le tre varianti Resource in Angular 20
Angular 20 offre tre modalità per caricare dati asincroni come signal. Ogni variante si rivolge a un caso d'uso diverso, ma tutte condividono lo stesso modello reattivo: dichiarare le dipendenze, definire un loader e consumare il risultato attraverso i signal.
resource()funziona con le Promise. Ideale quando si usafetch()o qualsiasi API basata su Promise.rxResource()funziona con gli Observable. La scelta giusta quando servono operatori RxJS comedebounceTime,retryoswitchMap.httpResource()avvolge direttamenteHttpClientdi Angular. Interceptor, utility di test e validazione dello schema funzionano senza configurazioni aggiuntive.
La differenza fondamentale tra httpResource e le altre due varianti: httpResource utilizza HttpClient internamente, il che significa che gli interceptor esistenti continuano a funzionare. L'API originale resource() bypassava completamente HttpClient, un punto dolente significativo in Angular 19.
Costruire un profilo utente con resource()
La funzione resource() accetta un calcolo params e una funzione loader. Quando i signal all'interno di params cambiano, il loader viene rieseguito automaticamente.
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
}
}Il parametro abortSignal consente ad Angular di annullare le richieste in corso quando userId cambia prima che la richiesta precedente sia completata. Questo previene le race condition senza gestione manuale delle sottoscrizioni.
Data fetching reattivo con httpResource
httpResource elimina il codice boilerplate combinando dichiarazione URL ed esecuzione HTTP in una singola chiamata. Restituisce un HttpResourceRef che espone value, isLoading, error, status e headers come 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
}
}Alcuni dettagli sono importanti. La funzione passata a httpResource restituisce un oggetto di configurazione della richiesta. Angular traccia le letture dei signal all'interno di questa funzione, quindi cambiare category attiva una nuova richiesta GET. Se una richiesta è già in corso, Angular la annulla prima di avviarne una nuova.
httpResource è progettato per il recupero dati (richieste GET). Utilizzarlo per operazioni POST, PUT o DELETE è rischioso perché l'annullamento delle richieste potrebbe interrompere le mutazioni a metà esecuzione. Per le operazioni di scrittura, utilizzare HttpClient direttamente o incapsulare le mutazioni in un metodo del service.
Validazione dello schema con Zod e httpResource
Le risposte API di servizi esterni possono divergere dalle strutture dati attese. L'opzione parse di httpResource integra librerie di validazione dello schema come Zod per intercettare le discrepanze a runtime invece di propagare silenziosamente dati corrotti.
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 }
);
}Quando l'API restituisce dati che non corrispondono a OrderSchema, la resource passa allo stato 'error'. Il tipo di ritorno della funzione parse determina anche il tipo TypeScript di value(), quindi le definizioni dello schema fungono sia da validatori a runtime che da generatori di tipo.
rxResource con Stream e Params in Angular 20
Angular 20 rinomina loader in stream e request in params per rxResource. Questa modifica allinea la nomenclatura con la semantica di streaming che rxResource supporta. La funzione stream riceve un contesto Observable e deve restituire un 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 },
}),
});
}A differenza di httpResource, rxResource offre il controllo completo sulla pipeline Observable. Operatori come debounceTime o retry possono essere concatenati all'interno di stream. Tuttavia, per il caso più comune (singola richiesta GET), httpResource richiede meno codice.
Pronto a superare i tuoi colloqui su Angular?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Tracciamento dello stato: le stringhe letterali sostituiscono gli enum
Angular 20 cambia ResourceStatus da un enum numerico a un tipo union di stringhe. I sei valori possibili forniscono una visione granulare del ciclo di vita della resource:
| Stato | Significato |
|---|---|
| 'idle' | params ha restituito undefined — nessuna richiesta effettuata |
| 'loading' | Prima richiesta in corso |
| 'reloading' | Richiesta successiva dopo un precedente successo |
| 'resolved' | Dati disponibili in value() |
| 'error' | Richiesta fallita — error() contiene l'errore |
| 'local' | Valore impostato localmente tramite .set() o .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();
},
});
}Restituire undefined da params imposta lo stato su 'idle' e impedisce l'esecuzione del loader. Questo pattern funziona bene per il data fetching condizionale — mostrare un messaggio fino a quando l'utente fornisce un input, poi caricare i dati.
A partire da Angular 20, chiamare value() su una resource in stato 'error' lancia un'eccezione a runtime. Le letture devono sempre essere protette con hasValue() o verificando status() prima di accedere a value(). Questa è una breaking change rispetto ad Angular 19, dove value() restituiva undefined in caso di errore.
Domande da colloquio su httpResource in Angular 20
La Resource API sta diventando un argomento standard nelle domande da colloquio su Angular. Le seguenti domande verificano una comprensione reale che va oltre la familiarità superficiale con l'API.
D: Quale problema risolve httpResource che resource() non risolve?
resource() utilizza fetch() o qualsiasi loader basato su Promise, bypassando HttpClient di Angular. Questo significa che gli interceptor (per token di autenticazione, logging, gestione errori) non vengono applicati. httpResource utilizza HttpClient internamente, quindi interceptor, utility di test (HttpTestingController) e funzionalità come withFetch() funzionano senza configurazione aggiuntiva.
D: Quando preferire rxResource a httpResource?
rxResource offre il controllo completo degli Observable attraverso la sua funzione stream. Va scelto quando la pipeline dati richiede operatori RxJS — debouncing dell'input di ricerca, retry con backoff esponenziale o combinazione di più stream con combineLatest. Per semplici richieste GET, httpResource richiede meno codice.
D: Come gestisce Angular le richieste concorrenti quando un signal cambia rapidamente?
Tutte e tre le varianti Resource annullano le richieste in corso quando params produce un nuovo valore. Per httpResource e resource(), l'AbortSignal annulla il fetch sottostante. Per rxResource, Angular annulla la sottoscrizione all'Observable precedente. Questo impedisce che risposte obsolete sovrascrivano dati aggiornati.
D: Qual è lo scopo dello stato 'local'?
Chiamare .set() o .update() su una resource ne modifica il valore localmente senza attivare il loader. Lo stato passa a 'local', indicando che il valore corrente non proviene dal server. Questo supporta gli aggiornamenti ottimistici dell'interfaccia — l'UI riflette immediatamente la modifica mentre una richiesta di mutazione separata viene eseguita in parallelo.
D: Come funziona l'integrazione Zod con httpResource?
L'opzione parse accetta qualsiasi funzione con la firma (data: unknown) => T. Quando la risposta HTTP arriva, httpResource passa il JSON analizzato attraverso parse prima di impostare value(). Se parse lancia un errore (ad esempio ZodError), la resource passa allo stato 'error'. Il tipo di ritorno di parse determina il tipo TypeScript di value(), fornendo sia sicurezza a runtime che tipi a tempo di compilazione da un'unica definizione di schema.
Per un approfondimento su Angular Signals e la loro integrazione nel framework, il modulo sui signal copre signal computati, effect e il modello di reattività.
Migrazione dalle sottoscrizioni HttpClient a httpResource
Le applicazioni Angular esistenti recuperano tipicamente i dati con sottoscrizioni HttpClient in ngOnInit o utilizzano AsyncPipe con Observable. La migrazione a httpResource segue un pattern prevedibile:
// 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()
}La migrazione elimina tutto il boilerplate di gestione del ciclo di vita. La pulizia delle sottoscrizioni avviene automaticamente quando il componente viene distrutto. Gli stati di caricamento e di errore sono integrati nella resource — non servono flag booleane separate.
Per le applicazioni che già utilizzano Standalone Components, la migrazione è semplice: sostituire l'iniezione di HttpClient e la logica di sottoscrizione con una singola dichiarazione httpResource.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Conclusione
httpResourcesostituisce le sottoscrizioni manuali diHttpClientcon una singola dichiarazione reattiva che gestisce caricamento, errori e annullamento automaticamente- Utilizzare
resource()per API basate su Promise,rxResource()per pipeline Observable con operatori RxJS ehttpResource()per chiamate HTTP standard con supporto agli interceptor - Angular 20 rinomina
requestinparamseloaderinstreamperrxResource— aggiornare il codice esistente di conseguenza - I valori di stato sono ora stringhe letterali (
'idle','loading','resolved','error','reloading','local'), sostituendo gli enum numerici di Angular 19 - L'opzione
parsedihttpResourceintegra Zod o Valibot per la validazione dello schema a runtime che genera anche i tipi TypeScript - Chiamare
value()su una resource in stato di errore lancia un'eccezione in Angular 20 — proteggere sempre conhasValue()o controllarestatus()prima - Tutte le varianti Resource annullano automaticamente le richieste in corso quando cambiano le dipendenze, prevenendo le race condition senza logica di annullamento manuale
Tag
Condividi
Articoli correlati

Domande di colloquio Angular 19: Signals, SSR e concetti imprescindibili
Le domande di colloquio Angular 19 più frequenti: Signals, idratazione incrementale, change detection senza Zone.js e nuove API reattive con esempi di codice e risposte attese.

Angular 19 Zoneless: Performance e Change Detection senza Zone.js
Guida tecnica approfondita alla Zoneless Change Detection in Angular 19 e 20. Funzionamento della reattività basata su Signals, attivazione di provideZonelessChangeDetection, insidie nella migrazione con setTimeout e Reactive Forms, SSR senza Zone.js e benchmark prestazionali.

Top 25 Domande di Colloquio Angular: Guida Completa al Successo
Le 25 domande di colloquio Angular più frequenti nel 2026. Risposte dettagliate, esempi di codice e consigli per ottenere il ruolo di sviluppatore Angular.