
Negli ultimi due anni, Angular ha intrapreso un percorso per rivoluzionare il suo modello reattivo, introducendo i signal come primitive fondamentali per la reattività sincrona all’interno del framework. Più recentemente, questo percorso si è esteso al dominio della reattività asincrona con la nuova Resource API.
Con gli ultimi aggiornamenti, Angular ha fatto un ulteriore passo avanti introducendo un tipo di risorsa specializzato: httpResource. Questa aggiunta estende la Resource API, offrendo un modo più efficiente per gestire le richieste HTTP.
In questo articolo esploreremo il funzionamento di httpResource e come utilizzarlo in scenari reali, partendo da un rapido ripasso della Resource API.
La Resource API
La Resource API di Angular è progettata per semplificare il caricamento asincrono delle risorse sfruttando i signal.
Offre un approccio semplificato alla gestione delle richieste di dati, al tracciamento degli stati di caricamento e all’aggiornamento automatico dei dati quando i signal da cui dipende la risorsa cambiano.
Come utilizzare una Resource?
Per utilizzare una resource, possiamo usare la funzione resource(), dove definiamo:
- Una funzione di request che restituisce i parametri per la richiesta asincrona;
- Una funzione di loader che recupera i dati in base ai parametri della request.
Ecco un semplice esempio:
import { resource, signal } from '@angular/core';
const RESOURCE_URL = 'https://jsonplaceholder.typicode.com/todos/';
private id = signal(1);
private myResource = resource({
request: () => ({ id: this.id() }),
loader: ({ request }) => fetch(RESOURCE_URL + request.id),
});
Code language: TypeScript (typescript)
Una resource tiene traccia automaticamente dei parametri della request e, ogni volta che vengono aggiornati, recupera nuovi dati utilizzando la funzione di loader.
La cosa più importante è che monitora lo stato dell’operazione di recupero dei dati, tenendo traccia del valore attuale della resource, dello stato e di eventuali errori.
Per maggiori dettagli sulla Resource API, inclusi il suo ciclo di vita, la gestione degli errori e come consumare una risorsa, potete consultare il mio precedente articolo:
Ora che abbiamo fatto un buon ripasso della Resource API, immergiamoci nella nuova httpResource. 💪🏻
httpResource
Una httpResource è un tipo specializzato di resource progettato per gestire le richieste HTTP e esporre tutte le informazioni relative alla richiesta tramite signals.
La funzione httpResponse( )
Per creare un httpResource possiamo utilizzare la funzione httpResource():
import { signal } from '@angular/core';
import { httpResource, HttpResourceRequest } from '@angular/common/http';
import { Todo } from './todo.model';
const RESOURCE_URL = 'https://jsonplaceholder.typicode.com/todos/';
id = signal(1);
staticHttpResource = httpResource<Todo>(
RESOURCE_URL + this.id()
);
reactiveHttpResource = httpResource<Todo>(
() => RESOURCE_URL + this.id()
);
configuredHttpResource = httpResource<Todo>(
{ url: RESOURCE_URL + this.id() } as HttpResourceRequest
);
reactiveConfiguredHttpResource = httpResource<Todo>(
() => ({ url: RESOURCE_URL + this.id() } as HttpResourceRequest)
);
Code language: TypeScript (typescript)
Nota: simile a una resource, una httpResource richiede un contesto di iniezione per essere creata. Puoi crearne uno come variabile di direttiva, istanziarlo all’interno del costruttore o fornire un contesto di iniezione specifico durante la creazione.
La funzione httpResource() richiede un solo parametro, che può essere:
- Una stringa, che rappresenta l’URL da cui recuperare i dati;
- Una funzione reattiva che restituisce una stringa, la resource recupererà i dati dal nuovo URL ogni volta che cambia a causa di un aggiornamento del signal;
- Un oggetto di tipo HttpResourceRequest, che consente di specificare dettagli aggiuntivi come intestazioni e corpo della richiesta;
- Una funzione reattiva che restituisce un oggetto HttpResourceRequest, la resource recupererà i dati con i nuovi parametri della richiesta ogni volta che questo oggetto cambia a causa di un aggiornamento del signal.
Questa flessibilità consente ad una httpResource() di adattarsi alle diverse esigenze dell’applicazione, semplificando il consumo asincrono delle risorse in modo dichiarativo.
All’interno del framework, il valore del parametro fornito viene utilizzato per generare una funzione che crea una HttpRequest. Questa funzione viene quindi passata all’implementazione base della resource come funzione request.
Fornendo funzioni reattive, httpResource può tenere traccia dei cambiamenti dei signal, garantendo che la resource si aggiorni dinamicamente quando le dipendenze cambiano.
Nota: puoi trovare una spiegazione dettagliata di HttpResourceRequest a fine articolo 👇🏻
Configura la Resource con HttpResourceOptions
La funzione httpResource() accetta un secondo parametro opzionale di tipo HttpResourceOptions, che ci consente di specificare le seguenti proprietà:
- defaultValue: il valore che la resource assumerà negli stati di Idle, Loading o Error. Se non specificato, la resource assumerà il valore predefinito di undefined;
- parse: una funzione che elabora la risposta grezza della richiesta http prima di assegnarla al valore della resource. Se non specificato, TypeScript dedurrà il tipo fornito tramite una type assertion.
Permette di eseguire validazioni, trasformazioni o l’applicazione di uno schema:
import { httpResource} from '@angular/common/http';
import { defaultUser, isValidUser, toUser } form './user.utils';
userResource =
httpResource<User>(() => `/users/${userId()}`, {
defaultValue: defaultUser,
parse: (raw) => {
if (!isValidUser(raw)) {
throw new Error("Invalid user data received");
}
return toUser(raw);
},
});
}
Code language: TypeScript (typescript)
- injector: sovrascrive l’Injector utilizzato dall’istanza di httpResource per distruggersi quando il componente o il servizio padre viene distrutto;
- equal: funzione di uguaglianza utilizzata per confrontare il valore della risposta.
Recuperare tipi di dati diversi da JSON
Di default, una httpResource assume che il tipo di risposta sia JSON.
Per richiedere un tipo di dato diverso, possiamo utilizzare varianti specifiche della funzione httpResource() fornite da httpResource:
- httpResource.text(): per gestire le risposte in formato testo semplice;
- httpResource.blob(): per gestire i dati binari, come immagini o file;
- httpResource.arrayBuffer(): per gestire i dati binari grezzi come ArrayBuffer.
import { httpResource } from '@angular/common/http';
// Fetch di testo semplice
textResource = httpResource.text(() => '/api/endpoint', {
defaultValue: ''
});
// Fetch di dati binari (come immagini)
imageResource = httpResource.blob(() => '/api/image', {
defaultValue: new Blob()
});
// Fetch di dati binari grezzi come ArrayBuffer
arrayBufferResource = httpResource.arrayBuffer(() => '/api/data', {
defaultValue: new ArrayBuffer(0)
});
Code language: TypeScript (typescript)
Integrazione con HttpClient e supporto per gli Interceptors
All’interno del framework, httpResource si basa su HttpClient, quindi per utilizzarlo è necessario fornirlo al tuo componente o servizio, solitamente tramite provideHttpClient o importando HttpClientModule.
Questa integrazione ci permette di sfruttare qualsiasi HttpInterceptor abbiamo configurato per la manipolazione delle richieste, la gestione dell’autenticazione e la gestione degli errori. In questo modo, possiamo integrare facilmente httpResource nelle nostre app esistenti, beneficiando appieno della configurazione HTTP già in uso.
Nota: l’implementazione di HttpClient potrebbe essere rimossa nei futuri aggiornamenti, ma è probabile che venga fornita una soluzione alternativa per garantire il funzionamento continuato. Quindi, tenete d’occhio i futuri aggiornamenti! 🫣
Casi d’uso nel mondo reale
La teoria è ottima, ma è la pratica che conta davvero.
Immergiamoci in alcuni esempi reali utilizzando httpResource.
Ottimizzazioni per Forms e Search
Quando si costruiscono form dinamici o funzionalità di ricerca, ottimizzare le richieste di dati è essenziale. Una delle strategie più comuni è ridurre le chiamate API non necessarie controllando quando e come vengono attivate le richieste.
Questo è facilmente realizzabile tracciando i cambiamenti dell’input e applicando tecniche per garantire che le richieste vengano effettuate solo quando l’utente ha smesso di digitare o quando il valore dell’input cambia effettivamente.
Quando gestiamo eventi basati su Observable, possiamo utilizzare operatori come debounceTime per ritardare le emissioni fino a quando non è passato un tempo specificato senza nuovi valori, o distinctUntilChanged per garantire che vengano emessi solo valori unici.
Tuttavia, poiché i signal non si basano sul concetto di tempo come gli Observable, queste strategie non possono essere applicate direttamente quando si utilizza httpResource.
Per aggirare questa limitazione, possiamo combinare la potenza dei signal e degli Observable sfruttando le funzioni toObservable() e toSignal() fornite dalla libreria @angular/core/rxjs-interop.

Questo ti permette di trasformare i signal in Observable, consentendoti di applicare operatori come debounceTime e distinctUntilChanged, e poi convertire i risultati di nuovo in un signal che possiamo utilizzare all’interno di httpResource.
import { signal } from '@angular/core';
import { httpResource } from '@angular/common/http';
import { toObservable, toSignal } from '@angular/core/rxjs-interop';
const RESOURCE_URL = 'https://jsonplaceholder.typicode.com/todos/';
id = signal(1);
// Converte il signal in un Observable e applica il debounceTime
idQuery$ = toObservable(this.id).pipe( debounceTime(3000) );
// Converte l'Observable in un signal
query = toSignal(this.idQuery$);
// Usa il signal query all'interno della httpResource
myResource = httpResource.text(
// This is triggered only after the debounce
() => RESOURCE_URL + this.query()
);
Code language: TypeScript (typescript)
Nota: l’operatore distinctUntilChanged non è necessario qui perché i signal evitano già di emettere a meno che il loro valore non cambi effettivamente. 🤓
Questo approccio ibrido ci offre la flessibilità di utilizzare strategie basate sul tempo mantenendo l’efficienza e la semplicità di httpResource e signal.
Ottimo! 🤩
Disabilitare la richiesta senza distruggere la risorsa
In alcuni casi, potremmo voler disabilitare la richiesta senza distruggere completamente la risorsa. Ad esempio, quando il recupero dei dati dovrebbe avvenire solo dopo un’azione specifica dell’utente, come il clic su un pulsante.
Invece di effettuare la richiesta immediatamente, possiamo abilitarla condizionalmente in base a determinate condizioni, in questo modo:
import { httpResource } from '@angular/common/http';
const RESOURCE_URL = 'https://jsonplaceholder.typicode.com/todos/';
id = signal(1);
shouldFetchData = signal(false);
myResource = httpResource<User[]>(() => {
const shouldFetchData = this.shouldFetchData(); // Custom condition
return shouldFetchData ? `${RESOURCE_URL}${this.query()}` : undefined;
});
Code language: TypeScript (typescript)
In questo esempio, httpResource attiverà la richiesta API solo se il signal shouldFetchData
ha valore true.
Se la condizione è false, la funzione restituisce undefined, impedendo qualsiasi chiamata API e resettando i valori della httpResource.
Restituire undefined non è l’ideale, ma fa comunque quel che ci serve. 😅
HttpResourceRequest in dettaglio
Fornire un oggetto HttpResourceRequest alla funzione httpResource() ci consente di specificare dettagli aggiuntivi per personalizzare la richiesta HTTP effettuata dalla httpResource, permettendoti di definire le seguenti proprietà:
- url: l’URL della richiesta. Non dovrebbe includere i parametri di query, che possono essere specificati tramite la proprietà dedicata params;
- method: il metodo HTTP per la richiesta. Per impostazione predefinita, il valore è GET;
- body: il body da includere nella richiesta. Se non viene specificata una Content-Type header, Angular cercherà di impostarne una in base al tipo del corpo;
- params: parametri di query da aggiungere all’URL, sia come HttpParams che come oggetto con coppie chiave-valore, dove i valori possono essere stringhe, numeri, booleani o array di questi tipi;
- headers: headers da includere con la richiesta, sia come HttpHeaders che come oggetto semplice con i nomi delle intestazioni come chiavi e valori di tipo stringa o array;
- context: dizionario di coppie chiave-valore per memorizzare informazioni aggiuntive contestuali per la richiesta, consentendo di trasportare metadati personalizzati per la gestione o il logging;
- reportProgress: se impostato su true, abilita gli eventi di progresso per la richiesta.
Questi eventi sono forniti tramite il HttpResource.progress signal; - withCredentials: specifica se impostare il flag withCredentials sulla richiesta in uscita. Quando è true, consente al browser di inviare cookie e informazioni di autenticazione con la richiesta;
- transferCache: configura la cache di trasferimento per il rendering lato server per la richiesta. Può essere un valore booleano o un oggetto con un array includeHeaders che specifica quali intestazioni includere nella cache di trasferimento;
Progettato solo per il recupero dei dati
Anche se possiamo definire un metodo HTTP diverso utilizzando HttpResourceRequest, il nuovo httpResource è progettato principalmente per il recupero dei dati, proprio come la Resource API fondamentale.
Questa flessibilità è fornita per consentire all’API di consumare dati che non necessariamente rispettano le specifiche HTTP standard.
Tuttavia, è importante notare che non è sicuro utilizzare un httpResource, o qualsiasi resource, per salvare o eliminare dati in questo modo, poiché ogni aggiornamento potrebbe interrompere le richieste precedentemente avviate. Di conseguenza, operazioni come POST, PUT o DELETE potrebbero non essere garantite per il completamento con successo.
Grazie per aver letto! 🫶🏻
Sono entusiasta e onorato di condividere che sono ufficialmente diventato un Google Developer Expert (GDE) in Angular! 🎉

Grazie a tutti per il vostro supporto, prometto che scriverò più frequentemente, perché ora la cosa si fa seria. 😜🚀
Non dimenticare di condividerlo con la tua community, amici tech o chiunque potrebbe trovarlo utile! 👋😁