I am trying to send 2 HTTP requests one by one; if the first one is succeeds, send the second one, if not display the corresponding error message regarding to the first request.
I am planning to use something like that, but not sure if it is the best option for this scenario:
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-root',
templateUrl: 'app/app.component.html'
})
export class AppComponent {
loadedCharacter: {};
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get('/api/people/1').subscribe(character => {
this.http.get(character.homeworld).subscribe(homeworld => {
character.homeworld = homeworld;
this.loadedCharacter = character;
});
});
}
}
I have different requests e.g. PUT and CREATE also using this approach. I know there are other ways e.g. forkjoin
, mergemap
, but if this one solves my problem seems to be more readable. Any idea?
First of all, your code works and that's great - you can leave it as is and everything will be fine.
On the other hand, there is a way for multiple improvements that will help you and your colleagues in future:
subscribe
s - not only you ignore the mighty power of Observable
s but also tie the code to a certain flow without an ability to reuse these lines somewhere in the application. Returning the Observable
might help you with "sharing" the results of the request or transforming it in some way.flatMap/mergeMap
, concatMap
and switchMap
work in a different way, providing you an ability to control the behaviour the way you want. Though, for http.get()
they work almost similar, it's a good idea to start learning those combining operators as soon as possible.Observable
s have a powerful mechanism of dealing with them, while .subscribe
allows you to handle an error only in one way.An example using the switchMap
:
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-root',
templateUrl: 'app/app.component.html'
})
export class AppComponent {
loadedCharacter: {};
constructor(private http: HttpClient) {}
ngOnInit() {
const character$ = this.http.get('/api/people/1').pipe(
tap(character => this.characterWithoutHomeworld = character), // setting some "in-between" variable
switchMap(character => {
return this.http.get(character.homeworld).pipe(
map(homeworld => {
return {
...character,
homeworld: homeworld
}
}
)
)
}),
catchError(errorForFirstOrSecondCall => {
console.error('An error occurred: ', errorForFirstOrSecondCall);
// if you want to handle this error and return some empty data use:
// return of({});
// otherwise:
throw new Error('Error: ' + errorForFirstOrSecondCall.message);
})
);
// you can either store this variable as `this.character$` or immediately subscribe to it like:
character$.subscribe(loadedCharacter => {
this.loadedCharacter = loadedCharacter;
}, errorForFirstOrSecondCall => {
console.error('An error occurred: ', errorForFirstOrSecondCall);
})
}
}
2 nested subscriptions are never a way to go. I recommend this approach:
this.http.get('/api/people/1').pipe(
switchMap(character => this.http.get(character.homeworld).pipe(
map(homeworld => ({ ...character, homeworld })),
)),
).subscribe(character => this.loadedCharacter = character);
Edit: For your university
this.http.get('/api/people/1').pipe(
switchMap(character => this.http.get(character.university).pipe(
map(university => ({ ...character, university})),
)),
).subscribe(character => this.loadedCharacter = character);
Or even chain university and homeworld requests
this.http.get('/api/people/1').pipe(
switchMap(character => this.http.get(character.homeworld).pipe(
map(homeworld => ({ ...character, homeworld })),
// catchError(err => of({ ...character, homeworld: dummyHomeworld })),
)),
switchMap(character => this.http.get(character.university).pipe(
map(university => ({ ...character, university})),
)),
).subscribe(character => this.loadedCharacter = character);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With