Issue
I want to make multiple HTTP calls with the same endpoint passing different id. Is there a better way to handle this from UI. We cannot change the backend right now, is there a better way?
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, forkJoin } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: 'app/app.component.html'
})
export class AppComponent {
loadedCharacter: {};
constructor(private http: HttpClient) {}
ngOnInit() {
let character1 = this.http.get('https://swapi.co/api/people/1');
let character2 = this.http.get('http://swapi.co/api/people/2');
forkJoin([character, character2]).subscribe(results => {
// results[0] is our character
// results[1] is our character2
});
}
}
Solution
There are essentially 2 options:
Is the option you have where you use
forkJoin()
to combine all the observables into an array- PROS: you know you will have all your data loaded inside you
subscribe()
- CONS: you have to wait for every HTTP request to finish before
forkJoin
emits the value - Note: you can implement a nice helper function like @Prince recommended
- PROS: you know you will have all your data loaded inside you
You can use
mergeMap()
for your IDs and react whenever one of the observables completes- PROS: you don't have to wait for every HTTP request to complete. You can react as they complete. Also, you can handle errors easier (so if one request fails you can still continue with the other requests).
- CONS: you have to handle your emitted values in the
.subscribe()
a little differently
At the end of the day, you will need to decided which approach is best for you. There isn't anything wrong with the implementation you have. I noticed you are keeping your loadedCharacter
as an object, so honestly option 2 may be good for your use case
Example code for Option 2. See this stackblitz for a small demo:
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, from, Subject } from 'rxjs';
import { mergeMap, takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-root',
templateUrl: 'app/app.component.html'
})
export class AppComponent {
private endSubs$ = new Subject();
loadedCharacter: {};
constructor(private http: HttpClient) {}
ngOnInit() {
/* ids of all the characters you want to load*/
const characterIds = [1, 2];
/* this will emit each id as a value */
from(characterIds).pipe(
/* merge each id to an Observable of the http get request */
mergeMap(id => this.http.get(`https://swapi.co/api/people/${id}`)),
/* cancel any pending requests when the component unloads.
this will avoid any RxJS memory leaks */
takeUntil(this.endSubs$)
).subscribe(
character => {
/* note that you will only receive 1 character at a time */
console.log('received character', character);
this.loadedCharacter[character.id] = character; // or whatever you want to do
},
err => console.log('Error loading a character', err),
() => console.log('All character requests have finished')
);
}
/* clean up our subscriptions when the component destroys */
ngOnDestroy() {
this.endSubs$.next();
this.endSubs$.complete();
}
}
EDIT: I added some RxJS cleanup code to avoid any memory leaks from the mergeMap
. Any requests that are pending when this component unloads will be cancelled. Here is an example SO answer explaining Observable cleanup, and here is a relevant RxJS article on where to place your takeUntil()
.
Answered By - DJ House
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.