Table of Contents
Introduction
Resource API has some changes in Angular 20.
-
requestwill be renamed toparams -
loaderis renamed tostreamin rxResource. Therefore,streamwill be used for streaming and querying a resource. - ResourceStatus is a string union type.
In this blog post, I will show an example of using rxResource to paginate Pokemons.
The first example updates the URL reactively when previous or next buttons are clicked The rxResource function responds to the new URL by querying the Pokemons and displaying the results.
Query a Resource
- Create a Pokemon Page Service
First, I define a PokemonPageService that pageinates Pokemons. The HTTP service makes a request to the Pokemon API to retrieve an array of Pokemon URL. Next, I use forkJoin to make multiple HTTP requests to retrieve the Pokemons. Finally, I combine the results so that the response includes the Pokemon count, the optional Previous URL, the Next URL, and an array of Pokemons.
@Injectable({
providedIn: 'root'
})
export class PokemonPageService {
private readonly http = inject(HttpClient);
paginate(url: string): Observable<PokemonPageType> {
return this.http.get<PokemonPageUrlType>(url).pipe(
mergeMap((res) => {
const pokemonUrls = res.results.map(({ url }) => url);
const pokemons$ = pokemonUrls.map((pokemonUrl) =>
this.http.get<PrePokemon>(pokemonUrl)
.pipe(catchError(() => of(undefined)))
)
return forkJoin(pokemons$).pipe(
map((pokemons) =>
pokemons.filter((pokemon) => !!pokemon)
),
map((results) => ({ ...res, results }))
)
})
)
}
}
- Create rxResource to Retrieve Pokemons
The PokemonPageComponent creates a rxResource that calls the PokemonPageService whenever the url signal is updated. The request property is renamed to params and it is a reactive function that returns the value of the url signal. The loader property is renamed to stream. The function destructures the parameter and renames params to url. The url is passed to the service to obtain the Pokemons to display.
// When URL updates, the rxResource retrieves a page of Pokemons
pokemonPageRef = rxResource<PokemonPageType, string>({
params: () => this.url(),
stream: ({ params: url }) => this.pokemonPageService.paginate(url),
defaultValue: {
count: 0,
previous: '',
next: '',
results: []
},
});
- The
urlsignal is updated when users click the Previous or Next buttons.
@if (pokemonPageRef.hasValue()) {
@let pagination = pokemonPageRef.value();
@for (result of pagination.results; track result.id) {
<app-pokemon-row [result]="result" />
<hr />
}
<div>
@if (prevUrl()) {
<button (click)="url.set(prevUrl())">Prev Page</button>
}
@if (nextUrl()) {
<button (click)="url.set(nextUrl())">Next Page</button>
}
</div>
}
- The resource status is a string in v20. The possible values are
idle,loading,reloading,error,resolved, andlocal.
@let status = pokemonPageRef.status();
@let isLoading = status === 'loading';
@let isResolved = status === 'resolved';
@let isReloading = status === 'reloading';
<p>{{ `Status: ${status}` }}</p>
<p>{{ `Is loading: ${isLoading}` }}</p>
<p>{{ `Is reloading: ${isReloading}` }}</p>
<p>{{ `Is resolved: ${isResolved}` }}</p>
<button (click)="this.pokemonPageRef.reload()">
Reload
</button>
When users click the previous or next button, the rxResource queries Pokemons and the status changes to loading. isLoading is true, isResolved and isReloading are false. When the resource returns the data successfully, the status becomes resolved. isLoading and isReloading are false, and isResolved is true.
When users click the Reload buttton, the rxResource invokes the reload method. The status first changes to reloading. isReloading is true, isLoading and isResolved are false. The last status is resolved, isReloading and isLoading are false, and the isResolved is true.
- This is the complete listing of the
PokemonPageComponent
@Component({
selector: 'app-pokemon-page',
imports: [PokemonRowComponent],
templateUrl: './pokemon-page.component.html',
})
export class PokemonPageComponent {
url = signal('https://pokeapi.co/api/v2/pokemon?limit=6');
pokemonPageService = inject(PokemonPageService);
// When URL updates, the rxResource retrieves a page of Pokemons
pokemonPageRef = rxResource<PokemonPageType, string>({
params: () => this.url(),
stream: ({ params: url }) => this.pokemonPageService.paginate(url),
defaultValue: {
count: 0,
previous: '',
next: '',
results: []
},
});
value = computed(() => this.pokemonPageRef.hasValue() ?
this.pokemonPageRef.value() : undefined
);
nextUrl = computed(() => this.value()?.next);
prevUrl = computed(() => this.value()?.previous);
}
Demos
Resources
- The PR relevant to the Resource API: PR60919
- Resource Doc: https://next.angular.dev/guide/signals/resource#
- resource API: https://next.angular.dev/api/core/resource#
- rxResource API: https://next.angular.dev/api/core/rxjs-interop/rxResource#
Top comments (0)