4
votes

I have a service that outputs 2 observables. 1 is for just getting the currently authenticated user and the other is for getting all of the active users. On the main screen I want to call both of these observables to use, this will be done (currently) by subscribing to both of these observables, however the first observable does not always run. I am looking for a solution for both observables to emit a value before anything errors our or loads on the page. I have tried forkjoin in my examble below but it does not work, i suspect this is because forkjoin expects something to emit but nothing emits, but subscribing to both observables does output the data I need. here is what I have tried:

    const tt =  Observable.forkJoin(
        this.auth.getActiveUsers().map((res) => res = res  ),
        this.auth.user.map((res) => res = res ),        
    );
    console.log("running" ); 
    tt.subscribe( (data) => {
      console.log("data ", data );      
        console.log("data[0] ", data[0] );
        console.log("data[1] ", data[1] );        
      },
      err => console.error(err),
      () => console.error("complete"),
    );  

and here is how the subscriptions are laid out in my service.ts file:

    Filename: Service.ts

    ...

    // first Observable  
    constructor( private afAuth: AngularFireAuth,
                private db: AngularFirestore ) {

        this.user = this.afAuth.authState.switchMap(user => {
            if (user) {
                return this.db.doc<iUser>(`users/${user.uid}`).valueChanges()
            } else {
                return Observable.of(null)
            }
            })     

    }

    ...

    // second Observable
    getActiveUsers(): Observable<iUser[]> {
    return this.db.collection<iUser>('users').valueChanges().catch(this.catchError);                 
    }

    ...
3

3 Answers

1
votes

forkJoin expects an array, so I think that's why it didn't work in your code:

 const tt = Observable.forkJoin([
                this.auth.getActiveUsers(),
                this.auth.user
            ]);
 // ...
1
votes

I guess your auth.user observable is a hot one (not complete when the value is emitted) while forkJoin emits only when all passed observables are complete. Not emit a value but complete.

If my guess is correct what you can do in this case is just passing auth.user.first() which will create a complete observable from the original observable and your forkJoin will work as expected

0
votes

Use mergeMap + forkJoin:

import { mergeMap } from 'rxjs/operators';
import { forkJoin, of } from 'rxjs';

const myPromise = val =>
  new Promise(resolve =>
    setTimeout(() => resolve(`Promise Resolved: ${val}`), 5000)
  );

const source = of([1, 2, 3, 4, 5]);
const example = source.pipe(mergeMap(q => forkJoin(...q.map(myPromise))));
/*
  output:
  [
   "Promise Resolved: 1",
   "Promise Resolved: 2",
   "Promise Resolved: 3",
   "Promise Resolved: 4",
   "Promise Resolved: 5"
  ]
*/
const subscribe = example.subscribe(val => console.log(val));

Full example