Mergemap - More Than 2 Observables

1.3k Views Asked by At

I got a service method working that would get all of the items from a collection, and merge in user data from a different user collection so that it could easily be displayed on the front end.

I tried to add a third layer of observables to pull and merge another set of data, but it's now just returning observable data to the front end.

This is the call that is working with adding in the users.

  getEventList(pid: string, sid: string) {

    return this.afs.collection(this.partyCollectionPath + pid + this.sessionCollectionPath + sid + this.eventCollectionPath,
      ref => ref.orderBy('timestamp', 'desc')).snapshotChanges().pipe(
        map(actions => {
          return actions.map(a => {
            const data = a.payload.doc.data({ serverTimestamps: 'estimate' }) as events_list;
            const id = a.payload.doc.id;

            return this.afs.doc("users/" + data.uid).snapshotChanges().pipe(map(a => {
             const useData = a.payload.data() as User
             return { id, ...data, user: { ...useData}}
            }))
          })

        }
        ),
        mergeMap(obs => combineLatest(obs))
      )
  }

And this is the code where I tried to add a third layer in to look at the inventoryID from the event. The console.log('iData', iData) isn't actually even hitting, and doesn't even appear in my console when I subscribe to this method so my final return isn't coming through. What did I miss here, can you not add another layer to this?

  getEventList(pid: string, sid: string) {

    return this.afs.collection(this.partyCollectionPath + pid + this.sessionCollectionPath + sid + this.eventCollectionPath,
      ref => ref.orderBy('timestamp', 'desc')).snapshotChanges().pipe(
        map(actions => {
          return actions.map(a => {
            const data = a.payload.doc.data({ serverTimestamps: 'estimate' }) as events_list;
            const id = a.payload.doc.id;

            return this.afs.doc("users/" + data.uid).snapshotChanges().pipe(map(a => {
              const useData = a.payload.data() as User

              return this.afs.doc(this.partyCollectionPath + pid + "/Party-Inventory/" + data.inventoryId).snapshotChanges().pipe(map(b => {
                const iData = b.payload.data() as inventory_list
                console.log('iData', iData)
                return { id, ...data, user: { ...useData }, inventoryData: { ...iData } }
              }))

            }))
          })

        }
        ),
        mergeMap(obs => combineLatest(obs))
      )
  }

Update This is what I got working for this scenario. In the end, I only wanted to run the combineLatest once, so the second mergemap from the answer was absolutely correct, I just needed to move my second internal observable call and final return value in there, which is then picked up by the outer observable mergeMap and combineLatest.

  getEventList(pid: string, sid: string) {

return this.afs.collection(this.partyCollectionPath + pid + this.sessionCollectionPath + sid + this.eventCollectionPath,
  ref => ref.orderBy('timestamp', 'desc')).snapshotChanges().pipe(
    map(actions => {
      return actions.map(a => {
        const data = a.payload.doc.data({ serverTimestamps: 'estimate' }) as events_list;
        const id = a.payload.doc.id;

        return this.afs.doc("users/" + data.uid).snapshotChanges().pipe(map(a => {
         const useData = a.payload.data() as User

         return { ...useData }
        }), mergeMap( a => {
          return this.afs.doc(this.partyCollectionPath + pid + "/Party-Inventory/" + data.typeId).snapshotChanges().pipe(map(b => {
            const iData = b.payload.data() as inventory_list
            return { id, ...data, user: { ...a }, inventoryData: { ...iData } }
          }))
        }))
      })

    }
    ),
    mergeMap(obs => combineLatest(obs))
  )

}

1

There are 1 best solutions below

4
On BEST ANSWER

It looks like the two inner observables should also be merged with a mergeMap() operator. I wrote a small function resembling yours and could not see the actual data when using 3 observables (only observable objects in the console).

Adding another mergeMap() within the pipe function where you fetch the specified user document solved the problem on my testing. Here is the sample code I used to test this.

this.afs.collection('cars', ref => ref.orderBy('model', 'desc')) // Querying documents from a 'cars' collection
      .snapshotChanges().pipe(
        map(carDocs => {
          return carDocs.map(car => {
            const carData: any = car.payload.doc.data();

            return this.afs.doc('users/' + carData.owner).snapshotChanges().pipe( // Fetching a specific user document
              map(userDoc => {
                const userData: any = userDoc.payload.data();
                
                return this.afs.doc('inventory/' + carData.name + 'Inventory').snapshotChanges().pipe( // Fetching inventory information
                  map(invEntry => {
                    const invData: any = invEntry.payload.data();
                    return {...carData, ...userData, ...invData};
                  })
                )
              })
            , mergeMap(obs => combineLatest(obs))) // Merges the two inner observables
          })
        }),
        mergeMap(obs => combineLatest(obs)) // Adds the outer observable to the combined observable
      ).subscribe(ev => console.log(ev));

Here is a thread discussing a similar implementation. Please let me know if this was helpful.