So I have an NgRx selector that returns an Observable with an array of Contacts. I would like to map over this stream, and for each array of contacts, map over each individual contact and make an Http request to Github to fetch their profile image, and append this to the Contact object. However, I am not sure how to do this without ending up with an Observable of an array of Observables.
Below is what I have tried, but this is not working.
this.contacts$: Observable<Contact[]> = this.store.select(getContacts).pipe(
map(contacts => {
return contacts.map(contact => {
return this.contactService.getGithub$(contact._id).pipe(
map(githubInfo => {
return {
...contact,
imageUrl: githubInfo.avatar_url
};
})
);
});
})
);
And below is the error message I am getting:
Type 'Observable<Observable<Contact>[]>' is not assignable to type 'Observable<Contact[]>'.
Type 'Observable<Contact>[]' is not assignable to type 'Contact[]'.
Type 'Observable<Contact>' is missing the following properties from type 'Contact': first_name, last_name, job_title, location, company ts(2322)
Any suggestions would be greatly appreciated!
Use switchMap
to map your contacts
array to an Observable that simultaneously executes your http requests and maps them to an extended Contact object.
this.contacts$: Observable<Contact[]> = this.store.select(getContacts).pipe(
switchMap(contacts => forkJoin(
contacts.map(contact => this.contactService.getGithub$(contact._id).pipe(
map(gitHubInfo => ({ ...contact, imageUrl: githubInfo.avatar_url }))
))
))
);
The issue here is that you are running an Array.map
which returns an Observable for each contact
. If you want the Observable to return the completed array, you'll want something like this:
this.contacts$: Observable<Contact[]> = this.store.select(getContacts).pipe(
// take the array and emit each value one by one
concatMap(contacts => from(contacts)),
// emit an array containing the contact and the corresponding githubInfo
concatMap(contact => forkJoin(of(contact), this.contactService.getGithub$(contact._id))),
// take the returned array and convert it to an Object
map(([contact, githubInfo]) => ({ ...contact, imageUrl: githubInfo.avatar_url})),
// concatenate everything into a final Array
toArray()
);
I must admit I've written this on the go, therefore without testing it, but I think it should work!
If maintaining the original order is important, keep concatMap
. If not, use mergeMap
to improve performance.
this.contacts$: Observable<Contact[]> = this.store.select(getContacts).pipe(
map(contacts => {
return contacts.map(contact => {
return this.contactService.getGithub$(contact._id).pipe(
map(githubInfo => {
return { ...contact, imageUrl: githubInfo.avatar_url };
})
);
});
}),
concatAll()
);
Just add concatAll
operator to pipe stream.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.