javascript - Transformera data i en Observable
Jag hämtar en lista med objekt från en Observable i min Angular-tjänst. Inuti varje objekt har jag en array med en lista över ämnen. För varje ämne behöver jag göra ett annat anrop för att få dess detaljer (t.ex. namn, beskrivning, osv.).
Datastruktur:
- post1
- subjects: ['books', 'cars', 'movies']
Med den listan har jag id från varje ämne men jag behöver returnera ett observerbart som innehåller alla ämnen som tillhör det inlägget (och deras detaljer).
AppService.ts
getPost(id: string): Observable<Post> {
return Observable.of({id: id, subjects: ['books', 'cars', 'movies']});
}
getSubjects(id: string): Observable<Subject[]> {
return this.getPost(id).pipe(
map((post: Post) => post.subjects),
mergeMap((subjects: string[]) => subjects.map((id: string) => this.getSubject(id))),
switchMap(list => list),
);
}
getSubject(id: string): Observable<Subject> {
switch (id) {
case 'books':
return Observable.of({id: 'books', name: 'Books'});
case 'cars':
return Observable.of({id: 'cars', name: 'Cars'});
case 'movies':
return Observable.of({id: 'movies', name: 'Movies'});
}
}
Det returnerar en ström av objekt men jag skulle vilja returnera en array med alla ämnen.
[DEMO]
Svar #1
Det beror på att du förväxlade .map() från Observables.map() och .map() från Array.prototype.map().
Denna specifika kodrad:
mergeMap((subjects: string[]) => subjects.map((id: string) => this.getSubject(id)))
returnerar inte en array av Subjects; istället returnerar den en array av Observables eftersom subject-argumentet inuti funktionen är en array, inte en Observable. Därför kommer just det .map() att returnera en array av returtypen av this.getSubject(id), vilket är en array av Observables; exakt det beteende du får.
Om du vill ha alla Observables lösta till en array av Subjects, måste du använda Observable.forkJoin. .forkJoin() kombinerar alla observables och kör dem parallellt.
getSubjects(id: string): Observable<Subject[]> {
return this.getPost(id).pipe(
map((post: Post) => post.subjects),
mergeMap((subjects: string[]) => Observable.forkJoin([...subjects.map((id: string) => this.getSubject(id))])),
);
}
Nu kan du använda det i din komponent:
getSubjects(id).subscribe(values=>{
console.log(values)//returns an array of Subjects
})
Observera att Observables.forkJoin måste vänta på att alla observables har slutförts innan en emission sker.
