简体   繁体   中英

RxJs - Having trouble resolving observable stream

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM