import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, mergeMap } from 'rxjs/operators';
import { forkJoin, from, Observable } from 'rxjs';
const baseUrl = 'https://hacker-news.firebaseio.com/v0/';
const topStoriesUrl = `${baseUrl}topstories.json`;
@Injectable({
providedIn: 'root'
})
export class StoriesService {
constructor(private http: HttpClient) { }
getItems(ids: number[]): Observable<any> {
return from(ids).pipe(
mergeMap(id => <Observable<any>> this.http.get(`${baseUrl}item/${id}`))
);
}
getTopIds(): Observable<any> {
return this.http.get(topStoriesUrl);
}
}
TS file:
constructor(private service: StoriesService) { }
topIds: number[] = [];
topNews: Array<Object> = [];
ngOnInit(): void {
this.getTopIds();
this.getTopNews();
console.log(this.topNews);
}
getTopIds() {
this.service.getTopIds().subscribe((data) => this.topIds = data);
}
getTopNews() {
this.service.getItems(this.topIds).subscribe(data => this.topNews = data);
}
topIds works perfectly but for some reason topNews returns empty array, not sure how to get this to work. Im stuck on this for a while. Also tried to do services with forkJoin but always failed to. Thanks for the help!
CodePudding user response:
Change getItems to :-
getItems(ids: number[]): Observable<any> {
const observables = ids.map((id) => this.http.get(`${baseUrl}item/${id}`);
return forkJoin(observables);
}
forkjoin basically will call all apis parallely and will return the response when all api completes in a array in same sequence the ids array converted into observables array.
CodePudding user response:
topIds works perfectly but for some reason topNews returns empty array, not sure how to get this to work. Im stuck on this for a while. Also tried to do services with forkJoin but always failed to.
You are calling this.service.getItems(this.topIds) even before this.topIds = data gets executed within the subscribe method.
HTTP calls are asynchronous, and hence the call to get the items should happen only once you have ids available i.e once the getTopIds() is completed.
You can try something as below:
this.myService.getTopIds().pipe(
tap(ids => { this.topIds = ids }), // required only if you need to store ids
switchMap(ids => this.myService.getItems(ids))
).subscribe(data => {
this.topNews = data;
console.log(this.topNews);
});
The above code will get the ids, and store it in this.topIds, and then make a call to get the items.
The getItems() logic should be as mentioned in one of the other answers:
getItems(ids: number[]): Observable<any> {
const observables = ids.map((id) => this.http.get(`${baseUrl}item/${id}`));
return forkJoin(observables);
}
