Angular v19 introduces a powerful new API for handling asynchronous data: the resource and rxResource APIs. These new primitives integrate seamlessly with Angular’s signal-based reactivity model, making it easier to fetch, manage, and update data while keeping track of loading and error states.
Why Use the Resource API?
Before resource, developers often relied on HttpClient with Observables or Promises to manage async data. While this approach worked well, it required additional RxJS operators like switchMap to handle reactive changes effectively. The new resource API simplifies this by:
- Automatically tracking dependent signals and reloading data when they change.
- Handling request cancellation to prevent race conditions.
- Providing built-in methods for local updates and manual refreshes.
- Offering
rxResourcefor developers who prefer working with Observables.
resource
1import { resource, Signal } from '@angular/core';2import { Component, computed, effect, signal } from '@angular/core';3
4interface MissionLog {5 id: number;6 title: string;7 description: string;8 status: 'pending' | 'completed';9}10
11@Component({ selector: 'app-mission-log', templateUrl: './mission-log.component.html' })12export class MissionLogComponent {13 missionId = signal(1);14
15 missionResource = resource({11 collapsed lines
16 request: this.missionId,17 loader: ({ request: id }) => fetch(`https://api.spacedata.com/missions/${id}`).then((res) => res.json()),18 });19
20 loading = this.missionResource.isLoading;21 error = this.missionResource.error;22
23 nextMission() {24 this.missionId.update((id) => id + 1);25 }26}Key Takeaways:
request: Tracks themissionIdsignal so the loader fetches new data when it changes.loader: Defines how the mission data is fetched.isLoadinganderror: Useful for UI updates.
Understanding Loaders
A loader is an asynchronous function that retrieves data when the request signal changes. Loaders receive an object with:
request: The computed value from therequestfunction.previous: The previous state of the resource, which includes the last known value and status.abortSignal: A signal used to cancel previous requests to avoid race conditions.
Example of using abortSignal in the loader:
1missionResource = resource({2 request: this.missionId,3 loader: ({ request: id, abortSignal }) =>4 fetch(`https://api.spacedata.com/missions/${id}`, { signal: abortSignal }).then((res) => res.json()),5});This ensures that if a new request is made before the previous one completes, the older request is aborted.
Resource Status
The resource API provides several status signals to help track the loading process:
IdleNo valid request; loader hasn’t run yet.LoadingLoader is currently running.ResolvedLoader has successfully completed and returned a value.ErrorAn error occurred while fetching data.ReloadingA new request is in progress while keeping the last successful value.LocalThe resource was updated locally via.set()or.update().
Reloading a Resource
The reload method allows you to manually re-fetch data from the loader without changing the request signal. This is useful when you want to refresh the data on demand, such as when a user clicks a refresh button. Calling reload() will re-trigger the loader while maintaining the last known successful value until the new data is fetched.
1refreshMission() {2 this.missionResource.reload();3}Updating Mission Data Locally
Sometimes, you need to modify data without fetching from the server again. The resource API allows local updates:
1completeMission() {2 this.missionResource.update(mission => ({ ...mission, status: 'completed' }));3}This marks the mission as completed locally, without making another API request.
rxResource
For developers who prefer Observables, rxResource provides a reactive approach using RxJS:
1import { rxResource } from '@angular/core/rxjs-interop';2import { HttpClient } from '@angular/common/http';3import { inject, signal } from '@angular/core';4
5@Component({ selector: 'app-rx-mission-log', templateUrl: './rx-mission-log.component.html' })6export class RxMissionLogComponent {7 http = inject(HttpClient);8 limit = signal(5);9
10 missionsResource = rxResource({11 request: this.limit,12 loader: (limit) => this.http.get<MissionLog[]>(`https://api.spacedata.com/missions?limit=${limit}`),13 });14}This integrates seamlessly with Angular’s existing HttpClient and RxJS ecosystem, automatically switching to the latest request when the limit changes.
Conclusion
The new resource and rxResource APIs are a game-changer for handling async data in Angular. They provide:
- Simple, declarative syntax for fetching data.
- Automatic request cancellation.
- Built-in support for local updates.
- A smooth transition for developers using RxJS.