0

I am trying to get all the data from firestore - collection and subcollection into an observable form of array and display it with async pipe.

availableCategoriesCollection: AngularFirestoreCollection<Category>;
availableCategories$: Observable<CategoryId[]>;
lstCategories: Observable<any>;

this.availableCategoriesCollection = this.httpDataService.getAllCategories();
this.availableCategories$ = this.availableCategoriesCollection.snapshotChanges().map(data => {
  return data.map(record => {
    const rec = record.payload.doc.data() as Category;
    const cId = record.payload.doc.id;
    return {cId, ...rec};
  });
});
this.lstCategories = this.availableCategories$.mergeMap(data => {
  const observables = data.map((rec: CategoryId) => {
    if (rec.hasSubCat) {
      return this.httpDataService.getSubCategory(rec.cId).snapshotChanges().map(d => {
        return d.map(r => {
          const arr: any = {};
          arr.id = r.payload.doc.id;
          arr.itemName = (r.payload.doc.data() as Category).categoryName;
          arr.category = rec.categoryName;
          return Observable.of(arr);
        });
      });
    }else {
      const arr: any = {};
      arr.id = rec.id;
      arr.itemName = rec.categoryName;
      arr.category = 'All';
      return Observable.of(arr);
    }
  });
  return Observable.forkJoin(observables);
});

and I've used <pre>{{lstCategories | async | json}}</pre> to display the data, but it is always null.

When I console.log(observables) before forkJoin I get (9) [ScalarObservable, Observable, Observable, ScalarObservable, ScalarObservable, ScalarObservable, ScalarObservable, ScalarObservable, Observable] out of which 3 of them which are Observable are subcategories and 6 of them which are ScalarObservable are main categories.

In spite of this data, the lstCategories doesn't get updated via async.

I've also tried to subscribe to lstCategories like

this.lstCategories.subscribe(data => {
     console.log(data);
});

but the above log never happens which means it is not getting subscribed. My knowledge on rxjs is very weak. Hope to find some help here.

Guruprasad J Rao
  • 29,410
  • 14
  • 101
  • 200
  • This is too long and too complicated. Reduce the question / code to the actual part that "breaks". It would also greatly improve your good to break it into several testable functions – Amit Mar 21 '18 at 05:47
  • @Amit. Sorry but, that was the only part that was causing issue. I've removed every unnecessary code from there and showed what I am trying to achieve and what I've so far. If I cut down any more then the question will be unclear. :( – Guruprasad J Rao Mar 21 '18 at 05:49
  • It is indeed a long question but can you change the return line of code block `arr.category = rec.categoryName; return Observable.of(arr);` to `return arr;` – Bunyamin Coskuner Mar 21 '18 at 05:51
  • @BunyaminCoskuner I've tried that but that will break the library which expects `Observable, Promise, Array, or Iterable`.. :( – Guruprasad J Rao Mar 21 '18 at 05:53
  • 1
    Are you trying to achieve anything like [this](https://stackoverflow.com/questions/47174075/firestore-how-to-get-the-collection-value-from-another-collection-document-id-is?answertab=votes#tab-top)? – Hareesh Mar 21 '18 at 07:49
  • @Hareesh - **The firebase/firestore master - _I've an eye on you_ :P** - Absolutely. But with slight modifications according to my needs. I've upvoted your answer. Could you please add it here so that I can edit the same accordingly? :) – Guruprasad J Rao Mar 21 '18 at 13:08
  • 1
    Not a master yet :), just a beginner. And only heard about Rxjs after working with Angularfire library. I will come up with an answer what i understood from your question. – Hareesh Mar 21 '18 at 14:23

3 Answers3

1

It seems that you are returning Observable of Observable of some array at following code block

map(d => 
...
arr.id = r.payload.doc.id;
arr.itemName = (r.payload.doc.data() as Category).categoryName;
arr.category = rec.categoryName;
return Observable.of(arr);
...

This block is already inside a map function of an Observable. When you return another Observable your whole return object looks like Observable<Observable<any[]>> Just change return line to return arr;

Bunyamin Coskuner
  • 8,719
  • 1
  • 28
  • 48
  • Yea.. Tried it, But still it doesn't get anything when I `subscribe` or `async`.. – Guruprasad J Rao Mar 21 '18 at 05:58
  • Also, why do you use `mergeMap`? If it is not for a particular reason, can you change it `flatMap`? – Bunyamin Coskuner Mar 21 '18 at 06:00
  • Well that was for one of the answers **[received here](https://stackoverflow.com/questions/49391733/value-from-observable-within-observable-using-angularfire2-firestore)** – Guruprasad J Rao Mar 21 '18 at 06:01
  • Here is the difference between them. https://tolikcode.github.io/post/rxjsMap/ if you want to wait for all the values you should use `flatMap`. I know RxJs is really confusing at first. Also, I'm still trying to understand where is the problem. – Bunyamin Coskuner Mar 21 '18 at 06:05
  • But as mentioned in the post `flatMap` is just an alias for `mergeMap` right? and also I tried with `flatMap`. Still it is not getting values. The one doubt am having is why is it not getting `subscribed` or working with `async`? – Guruprasad J Rao Mar 21 '18 at 06:07
  • Did you change the inner part? That has to be changed for sure. Because, when `rec` `hasSubCat` you are returning `Observable` of `Observable`s. Otherwise, just a single `Observable`. Keep that return line as I shown in my answer and continue from there. – Bunyamin Coskuner Mar 21 '18 at 06:10
  • Yes. I did that too. Now I am having `flatMap` instead of `mergeMap` and just returning `arr` inside `if` as you said. – Guruprasad J Rao Mar 21 '18 at 06:12
  • Also, can you try to subscribe both to `lstCategories` and `Observable.forkJoin(observables)` and console log the content? – Bunyamin Coskuner Mar 21 '18 at 06:12
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/167226/discussion-between-guruprasad-rao-and-bunyamin-coskuner). – Guruprasad J Rao Mar 21 '18 at 06:14
  • Buddy.. Thank you so much for all the help today. Really appreciate it. I have solved the problem. Please see below accepted answer. :) – Guruprasad J Rao Mar 21 '18 at 17:01
  • 1
    @GuruprasadRao You are welcome. I'm glad you solved it. The problem was with `forkJoin` after all huh :). But seriously though, you should try to simplify that code for the future. It's quite complex. – Bunyamin Coskuner Mar 21 '18 at 17:03
  • I agree with your point of simplifying the current code. I am open for suggestions though.. ;) Will have code clean up work in future.. :) But honestly, this is the least I could think of so far.. – Guruprasad J Rao Mar 21 '18 at 17:06
1

Try this way

this.lstCategories = this.availableCategoriesCollection.snapshotChanges().map(changes => {
      return changes.map(a => {
        const data = a.payload.doc.data() as Category;
        if(data.hasSubCat){
          const signupId = a.payload.doc.id;
          return this.httpDataService.getSubCategory(signupId).snapshotChanges().map(actions => {
            return actions.map(d => {
              return d;
            });
          }).map(signup => {
            return signup.map(md => {
              const arr: any = {};
              arr.id = md.payload.doc.id;
              arr.itemName = (md.payload.doc.data() as Category).categoryName;
              arr.category = data.categoryName;
              return arr;
            });
          });
        }else {
          const arr: any = {};
          arr.id = a.payload.doc.id;
          arr.itemName = data.categoryName;
          arr.category = 'All';
          return Observable.of(arr);
        }
      });
    }).flatMap(records => Observable.combineLatest(records)).map(data => {
      return [].concat(...data).map(d => {
        return d;
      })
    });
Guruprasad J Rao
  • 29,410
  • 14
  • 101
  • 200
Hareesh
  • 6,770
  • 4
  • 33
  • 60
  • 1
    Buddy.. I have finally got this working version. If you have any suggestions on the end result/code please let me know.. :) Thanks for your help.. – Guruprasad J Rao Mar 21 '18 at 17:03
  • 1
    I am glad my suggestion helped. can you explain this `[].concat(...data).map`? – Hareesh Mar 21 '18 at 17:36
  • 1
    The **[answer here](https://stackoverflow.com/questions/49407067/rxjs-move-array-of-objects-to-root-array/49408017#49408017)** explains pretty much about that. I was intending to move subcollection items to the main root object. and hence had to do with concat and spread syntax as per suggestion.. :) – Guruprasad J Rao Mar 21 '18 at 17:48
0

When you use map, you transform the response of a request. You needn't return an Observalbe

this.httpDataService.getSubCategory(rec.cId).snapshotChanges().map(d => {
        return d.map(r => {
          const arr: any = {};
          arr.id = r.payload.doc.id;
          arr.itemName = (r.payload.doc.data() as Category).categoryName;
          arr.category = rec.categoryName;
          return arr  //<--simply return arr
          // return Observable.of(arr); <--I think it's WORNG
        });
Eliseo
  • 50,109
  • 4
  • 29
  • 67