简体   繁体   中英

How to get Firestore collectionGroup datas as an Observable in Angular?

From the https://firebase.google.com/docs/firestore/query-data/queries documentation snippet, Im unable to get the results as an Observable

import { collectionGroup, query, where, getDocs } from "firebase/firestore";  

const museums = query(collectionGroup(db, 'landmarks'), where('type', '==', 'museum'));
const querySnapshot = await getDocs(museums);
querySnapshot.forEach((doc) => {
    console.log(doc.id, ' => ', doc.data());
});

So I wrote it like this:

const museums = query(
  collectionGroup(db, 'landmarks'),
  where('type', '==', 'museum')
);
const querySnapshot = await getDocs(museums);
const datas = querySnapshot.docs.map((doc) => {
  return doc.data() as Data;
});
console.log(datas);
return of(datas);

But I'm not getting results when subscribing to it or in the DOM using async pipe, but when I console.log(), there is an array of data.

component.html

{{ museums_data$ | async }}

component.ts

this.museums_data$.subscribe((datas) => console.log(datas));

I found it here: https://code.build/p/LWmSi3HfQzu5TMKPzb5p6i/angular-12-with-firebase-9 , which is written for the collection. Assuming it should also work for collection groups, I tried it, and it performed as expected.

This is a code snippet found in the link:

collectionData<Post>(
      query<Post>(
        collection(this.afs, 'posts') as CollectionReference<Post>,
        where('published', '==', true)
      ), { idField: 'id' }
    );

to:

collectionData<Landmark>(
      query<Landmark>(
        collectionGroup(db, 'landmarks') as CollectionReference<Landmark>,
        where('type', '==', 'museum')
      ), { idField: 'id' }
    );

At First glance code looks good but you are returning it incorrectly. Instead of mapping over you can return it directly using as Observable<Data[]> . Although I have not considered the error handling this can easily be achieved using the catchError rxjs operator.

You can use the following technique to get the data as you want using an async pipe.

data.service.ts :

import { Injectable } from '@angular/core';
import { collection, collectionData, Firestore, query, where
} from '@angular/fire/firestore';
import { Observable } from 'rxjs';
import { Data } from './data';

@Injectable({
  providedIn: 'root',
})
export class DataService {
constructor(private db: Firestore) {}
  getData(): Observable<Data[]> {
    const ref = collection(this.db, "landmarks");
    const q = query(ref, where("type", "==", "museum"));
    const data = collectionData(q, {
      idField: 'id',
    });
    return data as Observable<Data[]>;
  }

And use it like this in the corresponding component:

app.component.ts :

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { DataService } from './data.service';
import { Data } from './data';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  museums_data$!: Observable<Data[]>;
  constructor(private service: DataService) {}
  ngOnInit(): void {
    this.museums_data$ = this.service.getData();
  }
}

And

app.component.html :

<div *ngFor="let museum of museums_data$ | async">
  {{ museum.id }}
  //...
</div>

For more information there is this article that explains nicely.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM