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

39 Views Asked by At

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

2

There are 2 best solutions below

0
BizzyBob On BEST ANSWER

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' }))
  );

}
2
Robert Rendell On
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>;
  private user: User | undefined;
  constructor(
    private fs: Firestore,
    private _auth: AuthService
  ) {
    // currentUser$ is an Observable that holds the User data when available
    this.userObservable = this._auth.currentUser$;
    this.userObservable.currentUser$.subscribe((user) => {
       this.user = user;
    },
    (error) => {
       console.error(error);
    });

  }
  
  // example function
  getAll() {
    if (this.user) {
      // 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' });
    } else {
      alert('Action not possible. Not logged in');
    }
  }
}