Transform data in an Observable

天大地大妈咪最大 提交于 2021-01-28 09:42:04

问题


I'm retrieving a list of items from an Observable on my Angular service. Inside every item, I have an array with a list of subjects. For every subject, I need to make another call to get its details (e.g. name, description, etc.).

Data structure:

- post1
  - subjects: ['books', 'cars', 'movies']

With that list, I have the id from every subject but I need to return an observable containing all subjects belonging to that post (and their details).

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'});
    }
}

It returns a stream of objects but I'd like to return an array with all subjects.

[DEMO]


回答1:


That is because you confused .map() of Observables.map() and the .map() of Array.prototype.map().

This particular line of code:

 mergeMap((subjects: string[]) => subjects.map((id: string) => this.getSubject(id)))

does not return an array of Subjects; instead, it returns an array of Observables because the subject argument inside the function is an array, not an Observable. Hence, that particular .map() will return an array of the retyrn type of this.getSubject(id), which is an array of Observables; exactly the behaviour you are getting.

If you want to have all the Observables resolved into an array of Subjects, you will have to use Observable.forkJoin. .forkJoin() combines all the observables and fire them in parallel.

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))])),
    );
}

Now you can use it in your component:

getSubjects(id).subscribe(values=>{
    console.log(values)//returns an array of Subjects
})

Note that Observables.forkJoin will have to wait for all observables to complete before firing an emission.



来源:https://stackoverflow.com/questions/48553022/transform-data-in-an-observable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!