Search code examples
angulartypescriptfirebaseangularfire2angularfire

How to query for a single doc with AngularFirestore?


How can I build an Observable that's limited to a single doc?

This builds an Observable when querying for any number of docs:

foo.component.ts

import { AngularFirestore } from '@angular/fire/firestore';
import { Observable } from 'rxjs';

...

export class FooComponent implements OnInit {

...

    docs: Observable<any[]>;

    constructor(private db: AngularFirestore) {
        this.docs = db.collection('myCollection', ref => ref.where('name', '==', 'foobar')).valueChanges();

}

foo.component.html

<div *ngFor="let doc of docs | async">
    {{ doc.name }}, {{ doc.address }}
</div>

How can I do this when I know there's only 1 doc that'll be returned? I just won't know the doc ID in advance.

Something like the code above, except with just doc: Observable<any>; instead of docs: Observable<any[]>; so I don't have to use an *ngFor loop over the results?

I've tried

this.doc = db.collection('myCollection', ref => ref.where('name', '==', 'foobar').limit(1)).valueChanges();

Solution

  • Working stackblitz: https://stackblitz.com/edit/angular-v7yff2

    Based on https://stackblitz.com/edit/angular-lymeyp, you just need to do the following in the component

    crazinessToObserveASingleDoc() {
            this.singleDoc = this.db.collection('users', ref => ref.where('email', '==', this.email)).valueChanges().pipe(
              tap(docs => console.log('docs', docs)),
              map(val => val.length > 0 ? val[0] : null)
            )
        }
    

    and in the template

    <div>
        <span class="title">Document with email <span class="italics">{{ email }}</span>:</span>
        <div *ngIf="singleDoc | async as doc">{{ doc.name }}, {{ doc.email }}</div>
    </div>
    

    Previous solution

    Don't do take(1) as suggested in some comments: it completes the observable and then you will stop listening for changes (why using a realtime database then?). Also, you will still receive an array of results...

    If you don't know the id of the document, what you need to do is this:

    const doc: Observable<any | null> = 
       db.collection('myCollection', ref => ref.where('name', '==', 'foobar'))
         .valueChanges()
         .pipe(map(val => val.length > 0 : val[0] : null));
    
    

    where map if from rxjs/operators.