0

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.

1 Answer 1

1

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]) => {
Sign up to request clarification or add additional context in comments.

1 Comment

That was it, now it's working as expected. Thank you very much! @pierreduc can you please elaborate on why is the spread operator needed to actually resolve the Observable into an Object inside the combineLatest() operator?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.