We can use toSignal to convert any observable to a signal, we then use map to transform the string productId to a number.
readonly productId = toSignal<number>(
this._route.params.pipe(map((res: Params) => +res['productId']))
);
Once we have the productId signal, we can easily call the httpResource, this is an initializer API, so you can just initialize it once at the root of the class, no need to wrap it inside a computed or method.
readonly productResource = httpResource<any>(
() => `https://jsonplaceholder.typicode.com/todos/${this.productId()}`
);
The httpResource accepts a callback, since you are just making a GET call (default mode), just specify the url as the return value to the callback.
The signals used inside the URL will be used to trigger the reactivity, when the signals change, the URL is fetched again.
Then we use computed to get the value from the resource, which is overkill I think, but I guess it is there for a reason.
readonly product = computed(() => this.productResource.value());
Finally, we can use the methods error and isLoading to either show a error
or loading message, if something went wrong, or the data is still loading.
We finally use the computed to show the value.
@if(productResource.error()) {
Some error occourred.
} @else if(productResource.isLoading()) {
Loading...
} @else {
{{product() | json}}
}
Full Code:
import { Component, inject, computed } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { bootstrapApplication } from '@angular/platform-browser';
import {
ActivatedRoute,
provideRouter,
RouterLink,
RouterOutlet,
Params,
} from '@angular/router';
import { provideHttpClient, httpResource } from '@angular/common/http';
import { JsonPipe } from '@angular/common';
import { map } from 'rxjs';
@Component({
selector: 'app-child',
imports: [JsonPipe],
template: `
@if(productResource.error()) {
Some error occourred.
} @else if(productResource.isLoading()) {
Loading...
} @else {
{{product() | json}}
}
`,
})
export class Child {
private _route = inject(ActivatedRoute);
readonly productId = toSignal<number>(
this._route.params.pipe(map((res: Params) => +res['productId']))
);
readonly productResource = httpResource<any>(
() => `https://jsonplaceholder.typicode.com/todos/${this.productId()}`
);
readonly product = computed(() => this.productResource.value());
}
@Component({
selector: 'app-root',
imports: [RouterOutlet, RouterLink],
template: `
<a routerLink="/child/1">
Child 1
</a> |
<a routerLink="/child/2">
Child 1
</a> |
<a routerLink="/child/3">
Child 1
</a>
<div>
<router-outlet/>
</div>
`,
})
export class App {
name = 'Angular';
}
bootstrapApplication(App, {
providers: [
provideRouter([{ path: 'child/:productId', component: Child }]),
provideHttpClient(),
],
});