Search code examples
angularfirebaseangularfiregoogle-cloud-firestore

AngularFire2 firestore take(1) on doc valueChanges


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.


Solution

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