Search code examples
angularreduxrxjsobservablengrx

Combining multiple conditional Observables for preparing the data


I have two stores which I need to prepare the combined data

  1. To get all departments (for ex 50 departments)
  2. To get all users (ex: 1000 users)

I want to merge both the data and prepare final data. Note: the department would require all users to be loaded (which might take some time), and obliviously, users can't do anything on their own as they need department data to prepare the final result.

Here is for department:

      this.store.select(getUserInfo).subscribe((info) => {
        this.store.select(getAllDepts).subscribe((depts) => {
          if (depts.length < 1) {
            this.store.dispatch(loadDeptStore({ accountId: info.acntId}));
          } else {
            console.log(depts);  // get Dept data
          }
        });
      })

for Users data I have:

      this.store
        .select(getUserInfo)
        .pipe(
          flatMap((info) => {
            this.acntName = info.acntId;
            return this.store.select(getAllUsers, { accountId: info.acntId });
          })
        )
        .subscribe((res) => {
          if (res.length < 1 ) {
            this.store.dispatch(loadUsersInAccountStore({ billingCrmAccountId: this.acntName }));
          } else {
            console.log(res); // get users data
          }
        })

Shall I use combineLates or something else, if so, how should I do it in reactive way using RxJS features.


Solution

  • First, don't make a subscription in a subscription, it's an anti-pattern. Always use higher observables. I don't know exactly how the code work since I don't use ngrx but you could build your observables like that:

        const userInfo$ = this.store.select(getUserInfo);
        userInfo$.pipe(
            mergeMap(info => {
                this.acntName = info.acntId;
                const departments$ = this.store.select(getAllDepts)
                    .pipe(
                        map(depts => {
                            if (depts.length < 1) {
                                return this.store.dispatch(loadDeptStore({ accountId: info.acntId}));
                            } else {
                                return depts;  // get Dept data
                            }
                        })
                    );
                const users$ = this.store.select(getAllUsers, { accountId: info.acntId })
                    .pipe(
                        map(res => {
                            if (res.length < 1 ) {                          
                                return this.store.dispatch(loadUsersInAccountStore({billingCrmAccountId: this.acntName }));
                            } else {
                                return res; // get users data
                            }
                        })
                    );
                return combineLatest([departments$, users$]); 
            }),
            map(([depts, users]) => {
                // do stuffs and return something
            })
        ).subscribe(res => console.log('results: ', res));
            
    

    CombineLatest will emit when both your observables have emitted once. It will emit again each time one of the observables emits.
    It may not work with your current implementation but you get the idea on how to combine two observables from one source.