AngularFire2 firestore take(1) on doc valueChanges

majodi picture majodi · Oct 9, 2017 · Viewed 7.9k times · Source

Using AngularFire2 I try to get data from a single Firestore document. What works is:

this.addressRef = afs.doc<Address>('addresses/key')
this.addressRef.valueChanges().subscribe(val => {......}

I want to run the subscription code just once. Normally I use take(1) to achieve this (works with the Firebase DB). But this code:

this.addressRef.valueChanges().take(1).subscribe(val => {......}

gives error: TypeError: this.addressRef.valueChanges(...).take is not a function.

VS Code is listing the take action. Is this a bug in AngularFire2, am I doing this wrong or is there an other (better) way to stop the subscription after the data is fetched? I also tried topromise but this also failed.

Answer

DauleDK picture DauleDK · Oct 10, 2017

update

Because this answer has received a lot of attention, I have provided an updated answer. You should, in general, try to think reactively when programming with observables. There are some great tutorials, but this one is good.

New solution

user$ = this.afDs.doc<User>('users/NaGjauNy3vYOzp759xsc')
  .valueChanges().pipe(
    take(1) // Here you can limit to only emit once, using the take operator
  )

And in your template, you subscribe to your observable.

{{(user$ | async).name}}

Context

This way of solving the problem has multiple benefits:

  • If you wan't to transform data, you just throw in a map operator
  • You can filter events with the rxjs filter operator
  • Create other streams, based on the user$ stream
  • easy debugging, because other developers can see the flow of data in your app in a more transparent way

For example (creating another stream, that only emits if the user is admin, and creates a new title):

adminTitle$ = this.user$.pipe(
  filter(user => user.title === 'admin'),
  map(user => `${user.name} (admin)`)
)

Old answer

You can achieve this by doing the following:

this.userDoc = afDs.doc<User>('users/NaGjauNy3vYOzp759xsc');
this.userDoc.valueChanges()
  .pipe(take(1))
  .subscribe(v => {
    this.user = v;
  });