简体   繁体   中英

Angular Firestore combineLatest with multiple queries

I'm having a little trouble with Angular9, Firestore6 and the combineLatest rxjs operator.

I have one collection of users , and another collection of items . One user can have several items, but in an old fashioned manyToMany relationship way, in which the items are master data (non modifiable) and the bridge table joins both collections with additional data:

  • items

    • apple
      • name: "apple"
      • color: "green"
  • user

    • items
      • uid
        • uid: "apple"
        • quantity: 23

So I need to get a list of the items of the logged in user , joining both collections:

  • user
    • items
      • uid
        • uid: "apple"
        • quantity: 23
        • join
          • name: "apple"
          • color: "green"

This is my code:

import { AngularFireAuth } from '@angular/fire/auth';
import { AngularFirestore } from '@angular/fire/firestore';
import { switchMap, map } from 'rxjs/operators';
import { combineLatest, of } from 'rxjs';

getUserItems() {
  return this.angularFireAuth.authState.pipe(
    switchMap(user => {
      if (user) {
        return this.angularFirestore.collection<any>(`users/${user.uid}/items`).valueChanges()
          .pipe(
            switchMap(userItems=> {
              return combineLatest([
                of(userItems),
                userItems.map(userItem=> this.angularFirestore.doc<any>(`items/${userItem}`).valueChanges())
              ])
            }),
            map(([userItems, items]) => {
              console.log(userItems, items);
              return userItems.map(userItem => {
                return {
                  ...userItem,
                  join: items.find(item => item.uid === userItem.uid)
                }
              })
            })
          )
      }
    })
  )
}

But in the map(([userItems, items]) , instead of [any[], any[]] , I get [any[], Observable<any>] , it does not compile because Property 'find' does not exist on type 'Observable<any>' .

I guess it's because the second .valueChanges() is not getting resolved, but I don't know what I'm doing wrong. Can anyone help me out?

I already tried other answers like this one but with no luck so far.

Thanks in advance.

You were very close, you just need to add a spread operator to the userItems.map and you need to get the itemId (just a guess) from the userItem object:

return combineLatest([
  of(userItems),
  ...userItems.map(
    userItem => this.angularFirestore.doc<any(`items/${userItem.itemId}`).valueChanges()
  )
])

The combineLatest wants an array of Observables . Let's ignore the of(userItems) If you just do:

combineLatest([
  userItems.map((userItem) => this.af.valueChanges())
]) 

You will end up with an array inside of an array, so basically Observable[][] . That's why you need to use the spread operator, to put the items in the surrounding array. If you did not have the of(userItems) , you could have just done:

combineLatest(
  userItems.map((userItem) => this.af.valueChanges())
) 

So without the wrapping array, but because you need the userItems as well, it's a nice solution to add them to the top.

I do however noticed a couple of other mistakes, which will probably not make it work for you. The items in the last map, is just one item, and not an array. To get it into the array you expect, you again need to use the spread operator, which in this case is a rest operator, on the items parameter, like this:

map(([userItems, ...items]) => {

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