Search code examples
angulartypescriptrxjsangular15angular-observable

Which is the correct way to deal with an observed dependency?


I'm using Angular 15 and @angular/fire ^7.5.0 for my project.

I have a service that makes some calls to the Firestore database, but before i perform any action i need to recover some information from the current user that is provided by FireAuth by an Observable.

import {
  collection,
  Firestore,
  collectionData,
} from '@angular/fire/firestore';
import { AuthService } from '../auth/auth.service';
import { User } from '@angular/fire/auth';

export class DummyDataService {
  userObservable: Observable<User | null>;
  constructor(
    private fs: Firestore,
    private _auth: AuthService
  ) {
    // currentUser$ is an Observable that holds the User data when available
    this.userObservable = this._auth.currentUser$;
  }
  
  // example function
  getAll() {
    // this should be done after the user$ observable is filled with the real User
    const coll = collection(this.fs, `/${user.custom_data}/dummydata`);
    // return the data as an Observable
    return collectionData(coll, { idField: 'id' });
  }
}

So I'm asking which is the correct way to handle this kind of scenario


Solution

  • You can declare an observable that begins with the non-empty emission of userObservable, then map it accordingly:

    export class DummyDataService {
      userObservable: Observable<User | null>;
    
      constructor(
        private fs: Firestore,
        private _auth: AuthService
      ) {
        this.userObservable = this._auth.currentUser$;
      }
      
      getAll() {
        return this.userObservable.pipe(
            filter(user => !!user),
            map(user => collection(this.fs, `/${user.custom_data}/dummydata`)),
            switchMap(coll => collectionData(coll, { idField: 'id' }))
        );
      }
    }
    
    • filter is used to prevent emissions where user is empty.
    • map converts the user into the collection
    • switchMap will emit the emissions from its inner source (collectionData(...))

    Note, if the method doesn't take a parameter, you don't need to use a method that returns an observable, you can simply declare the observable as a property:

    export class DummyDataService {
    
      constructor(
        private fs: Firestore,
        private _auth: AuthService
      ) { }
      
      dummydata$ = this._auth.currentUser$.pipe(
        filter(user => !!user),
        map(user => collection(this.fs, `/${user.custom_data}/dummydata`)),
        switchMap(coll => collectionData(coll, { idField: 'id' }))
      );
    
    }