Merging collection and sub collection observables in Angularfire2

Issue

I have integrated stripe into my firebase project (Firestore) which through web hooks adds and updates the products I have setup in Stripe to the firestore database. To display available products I have an observable which is subscribed in the template using async pipe. This all works fine, however, each product document in the products collection has a sub collection called ‘prices’ which contains the current and previous prices. I am trying to figure out how to return this additional data for each product in the main products observable.

The code to return just the products is:


export interface Item { name: string; id: string; }

  watchProducts () {

      const productsCollection: AngularFirestoreCollection<Item> =
      this._firestore
      .collection('products', ref => {
        return ref.where('active', '==', true);
      });

      const currentProducts: Observable<Item[]> = productsCollection.snapshotChanges().pipe(
        map(actions => actions
          .map(a => {
            const data = a.payload.doc.data();
            const id = a.payload.doc.id;
            console.log('a.payload');
            console.log(a.payload);
            return { id, ...data };
          }),
        ),
      );
      return currentProducts;
  }

The template code is:

            <button *ngFor="let product of products | async" ">
                {{product.name}} £/ month
            </button>

The location of the prices collection is:

this._firestore
.collection('products')
.doc({productId})
.collection('prices', ref => {
        return ref.where('active', '==', true);
      })

I would like to merge the results so in the async ngFor I could access the current price with active === true. I’ve looked at switchMap and mergeMap but can’t seem to get anything to work properly. My knowledge is RxJs is somewhat limited.

Any help would be greatly appreciated.

Best A

Solution

You can create your own operator as Jeff introduced. But to simplify, you could manage it as following way:

const products$ = this._firestore
  .collection('products')
  .snapshotChanges()
  .pipe(
    map((actions: any[]) => actions.map((a) => ({ ...a.payload.doc.data(), ...{ id: a.payload.doc.id } }))),
    switchMap((products: any[]) => {
      const pricesCols$ = products.map((p) =>
        this._firestore
          .collection(`products/${p.id}/prices`, (ref) => ref.where('active', '==', true))
          .valueChanges()
      );

      // passing the products value down the chain
      return combineLatest([of(products), combineLatest(pricesCols$.length ? pricesCols$ : [of([])])]);
    }),
    map(([products, pricesCols]) =>
      products.map((p, idx) => {
        p.prices = pricesCols[idx];
        return p;
      })
    )
  );

Answered By – coturiv

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published