Table of Contents
Introduction
Resource API has some changes in Angular 20.
-
request
will be renamed toparams
-
loader
is renamed tostream
in rxResource. Therefore,stream
will 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
url
signal 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)