Search code examples
typescriptfirebasegoogle-cloud-firestoreangularfire2

How to observe a Firestore document with AngularFire? `onSnapshot() not working?


Using Angular 14 and AngularFire 7.4.1, I made an observer for a Firestore collection. It works great:

interface Scientist {
  name?: string | null,
  born?: number | null,
  accomplishment?: string | null
};

scientist$: Observable<Scientist[]>;

constructor(public firestore: Firestore) {
    this.scientist$ = collectionData(collection(firestore, 'scientists'));
}

The collection displays in the HTML view.

I see in the Firestore documentation that it's also possible to observe a single document. I'm a big fan of Charles Babbage so I want to observe this document:

interface Scientist {
  name?: string | null,
  born?: number | null,
  accomplishment?: string | null
};

scientist$: Observable<Scientist[]>;
charle$: Observable<Scientist>;

constructor(public firestore: Firestore) {
  this.scientist$ = collectionData(collection(firestore, 'scientists')); // works
  this.charle$ = onSnapshot(doc(firestore, 'scientists', 'Charles Babbage')); // doesn't work
}

That throws an error:

Type 'Unsubscribe' is not assignable to type 'Observable<Scientist>'.

Oh yeah, Firestore returns my data wrapped in a document. Let's change the data type to any:

charle$: Observable<any>;

That throws the same error. There seems to be something incompatible between Angular's Observable and Firestore's onSnapshot. Is there a AngularFire single document observer, e.g., documentData(doc(firestore, 'scientists', 'Charles Babbage')?

I have a second, related question. How do I detach the collection listener?


Solution

  • The onSnapshot() returns a function that can been called to detach the listener. It also takes a function as second param that'll trigger every time an update is received. Try refactoring the code as shown below:

    const unsub = onSnapshot(doc(firestore, 'scientists', 'Charles Babbage'), (snapshot) => {
      console.log("> Updated received", doc.data())
      // TODO: Update data state
    });
    

    You can then update the state from the function itself. Just call the unsub(); and the listener will be detached.