Yassine Fathi
Yassine Fathi

Reputation: 58

Merge multiple observables and return one

I have a function that should return an observable with a list of Conversations. To get that, I have to fetch the user conversations ids from another table Participants and combine the those fetch one by one.

public getConversations(user_id: string): Observable<Conversation[]> {
    return this.firebase
      .collection<Participant>(this.PARTICIPANTS_COLLECTION, ref =>
        ref.where('user_id', '==', user_id)
      )
      .valueChanges()
      .map((participants: Participant[]) => {
        const conversation_ids: string[] = [];
        participants.forEach((p: Participant) => conversation_ids.push(p.conversation_id));
        return conversation_ids;
      })
      .mergeMap((ids: string[]) => {
        let conversations: Observable<Conversation[]> = new Observable<Conversation[]>();
        for(let id of ids) {
          conversations.merge(
            this.firebase.collection<Conversation>(this.CONVERSATIONS_COLLECTION, ref =>
              ref.where('id', '==', id)
            ).valueChanges() // I want to merge these
          );
        }
        return conversations; // I want to return this
      });
  }

Which operator should I use to combine those observables?

Upvotes: 2

Views: 743

Answers (1)

CozyAzure
CozyAzure

Reputation: 8468

merge() will not work for firebase, because the Observable .valueChanges() never completes, and merge() requires all of the observable it merges to complete before it can emit the resultant value. From the docs:

merge subscribes to each given input Observable (as arguments), and simply forwards (without doing any transformation) all the values from all the input Observables to the output Observable. The output Observable only completes once all input Observables have completed. Any error delivered by an input Observable will be immediately emitted on the output Observable.

You will need to use Observable.combineLatest(). Also, you can use Javascript's native map() to eliminate the forloop inside your code:

return this.firebase.collection<Participant>(this.PARTICIPANTS_COLLECTION, ref => ref.where('user_id', '==', user_id)).valueChanges()
    .map((participants: Participant[]) => participants.map(p => p.conversation_id))
    .mergeMap((ids: string[]) => Observable.combineLatest(ids.map(id => this.firebase.collection<Conversation>(this.CONVERSATIONS_COLLECTION, ref => ref.where('id', '==', id)).valueChanges())));

Upvotes: 1

Related Questions