ARTICLE AD BOX
Since you are using Angular 20, You should really consider Resource API for this scenario, it is has error handling, reactivity on signal changes, loading and error state also. This greatly reduces the coding effort for API calls.
Using rxResource:
Below is an example of using rxResource which accepts an observable, which you can define on the service level.
@Injectable({ providedIn: 'root', }) export class UserService { private http = inject(HttpClient); constructor() { } //userByID should be called everytime profileUserID signal value is changed. public getUserDetails(profileUserID: number) { return this.http.get<User>(`myapi/user/${profileUserID}`}); } }Then we can simplify the params property to be fetched using toSignal.
export class Profile { route: ActivatedRoute = inject(ActivatedRoute); userService: UserService = inject(UserService); // auto unsubscribe of param profileUserID = toSignal( this.route.params.pipe( map((routeParams) => { return Number(routeParams['userID']); }) ) ); userDetailsResource = rxResource({ params: () => this.profileUserID() stream: (request: profileUserID) => this.userService.getUserDetails(profileUserID) }); }Using withComponentInputBinding:
You can also consider enabling withComponentInputBinding, which will simplify the fetching of the route params, with less code.
export class Profile { route: ActivatedRoute = inject(ActivatedRoute); userService: UserService = inject(UserService); // using withComponentInputBinding profileUserID = input.required<number>({ alias: 'userID' }); userDetailsResource = rxResource({ params: () => this.profileUserID() stream: (request: profileUserID) => this.userService.getUserDetails(profileUserID) }); }To use this, add the provider to the main.ts - bootstrapApplication config.
bootstrapApplication(AppComponent, { providers: [provideRouter(appRoutes, withComponentInputBinding())], });Using httpResource:
If you do not necessarily need the API observable at the service level, you can leverage httpResource to just directly call the API, with the least amount of code.
export class Profile { route: ActivatedRoute = inject(ActivatedRoute); userService: UserService = inject(UserService); // auto unsubscribe of param profileUserID = toSignal( this.route.params.pipe( map((routeParams) => { return Number(routeParams['userID']); }) ) ); // or // using withComponentInputBinding // profileUserID = input.required<number>({ alias: 'userID' }); userDetailsResource = httpResource(() => `myapi/user/${this.profileUserID()`); }HTML Usage:
For the HTML side of the implementation, we can use the state signals, isLoading and error to handle loading and error scenarios.
@if(userDetailsResource.error()) { Failed to fetch user details. } @else if (userDetailsResource.isLoading()) { Loading... } @else { {{ userDetailsResource.value() }} }