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?
CodePudding user response:
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.
CodePudding user response:
Here is the code based on Dharmaraj's answer.
import { Firestore, doc, collectionData, collection, onSnapshot } from '@angular/fire/firestore';
interface Scientist {
name?: string | null,
born?: number | null,
accomplishment?: string | null
};
export class AppComponent {
scientist$: Observable<Scientist[]>;
charle$: Scientist = {
name: null,
born: null,
accomplishment: null
};
unsubCharle$: any;
constructor(public firestore: Firestore) {
this.scientist$ = collectionData(collection(firestore, 'scientists')); // collection listener
this.unsubCharle$ = onSnapshot(doc(firestore, 'scientists', 'Charles Babbage'), (snapshot: any) => { // document listener
this.charle$.name = snapshot.data().name;
this.charle$.born = snapshot.data().born;
this.charle$.accomplishment = snapshot.data().accomplishment;
});
}
async detachListener() {
console.log("Detaching listener.");
this.unsubCharle$();
}
}
<h3>Observe (collection)</h3>
<ul>
<li *ngFor="let scientist of scientist$ | async">
{{scientist.name}}, born {{scientist.born}}: {{scientist.accomplishment}}
</li>
</ul>
<h3>Observe (single document, 'Charles Babbage')</h3>
<div *ngIf="charle$.name">{{ charle$.name }}, born {{ charle$.born }}: {{ charle$.accomplishment}}</div>
<form (ngSubmit)="detachListener()">
<button type="submit" value="detachListener">Detach Listener</button>
</form>
This makes two observers, a collection listener and a document listener. The document listener can be detached.