1
votes

What's a clean way of using ngFor to stream/render Firebase query results? My understanding is that ngFor with async pipe can render any changes to Firebase query results automatically (if written as an observable).

The following code produces this error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.

The error makes sense because the observable doesn't return an array of objects.

Template:

<div *ngFor="let wishlist of wishlists | async">
    <div>{{wishlist}}</div>
</div>

Component:

this.wishlists =
    Observable.fromPromise(
        firebase.database().ref('wishlists').once('value')
    );

Changing component code to the following at least renders but isn't an auto updating stream.

this.wishlists =
    new Promise((resolve, reject) => {
        firebase.database().ref('wishlists/').on("value", function (snapshot) {
        var wishlists = new Array(snapshot.numChildren());
        for (let data in snapshot) {
            let data = snapshot.val();
            let wishlist = new Wishlist(snapshot.key);
            wishlists.push(wishlist);
        }
        resolve(wishlists);
    });

Is it necessary to create an Observable e.g.

return Observable.create(observer => {
    let listener = ref.on('value', snapshot => {
        let data = snapshot.val();
        let wishlist: Wishlist;
        if (data != null) {
            wishlist = new Wishlist(snapshot.key);
        }
        observer.next(wishlist);
        // observer.complete(); // How do we know when complete or do we have to keep the stream open indefinitely?
    }, observer.error);
    return () => {
        ref.off('value', listener);
    }
});
1

1 Answers

0
votes

I don't know that this is the best way to achieve this, but I had a similar problem and just type cast the variable holding the Firebase snapshot as an Array. So in your case your code would look like this (assuming you've defined a Wish Class):

wishLists: Array<Wish>;
 ...
this.wishLists = firebase.database().ref('wishlists').once('value');

That should then render (though it won't work with the async pipe) with something like

<ion-header>
  <ion-navbar>
    <ion-title>Wishes</ion-title>
  </ion-navbar>
</ion-header>


<ion-content>
  <ion-list>
    <ion-item *ngFor="let wish of wishLists">
      {{wish.name}}
    </ion-item>
  </ion-list>
</ion-content>