Search code examples
angulartypescriptfirebasegoogle-cloud-firestoreangularfire2

Cannot find a differ supporting object. NgFor only supports binding to Iterables such as Arrays


I've inadvertently changed some code somewhere and now my posts are no longer being pulled through, can anyone spot the mistake?

Service:

  posts: AngularFirestoreCollection<any[]>;

  constructor(private db: AngularFirestore) { }

  getPosts() {
    this.posts = this.db.collection('/posts') as AngularFirestoreCollection<any[]>;
    return this.posts;
  }
}

Post Component:


  posts: AngularFirestoreCollection<any>;

  constructor(private firebaseService: FirebaseService) { }

  ngOnInit() {
    this.posts = this.firebaseService.getPosts();
  }

}

Post HTML:

  <mat-grid-tile *ngFor="let post of posts">
      <mat-card class="mat-elevation-z4">
          <img mat-card-image src="{{post.imgUrl}}">
          <mat-card-actions>
              <button mat-icon-button><mat-icon>thumb_up_alt</mat-icon></button><span class="counter mat-small">{{post.numberOfLikes}}</span>
              <button mat-icon-button><mat-icon>star</mat-icon></button><span mat-small class="counter mat-small">{{post.numberOfSaves}}</span>
          </mat-card-actions>
      </mat-card>
  </mat-grid-tile>
</mat-grid-list>``

Now I'm getting Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.


Solution

  • Here are some suggestions to fix your problem:

    1. AngularFirestoreCollection should be passed the item type of your collection, here you an array type (any[]), but something like Post would be more appropriate.
    2. You have to call valueChanges() or snapshotChanges() on your collection to get an observable of it. this.db.collection('/posts') only returns a reference to the collection (an object to manage it) and not the list itself.
    3. Then use the async pipe in the template to automatically subscribe/unsubscribe from the returned observable.

    In your service:

    export interface Post {
      imgUrl: string;
      numberOfLikes: number;
      numberOfSaves: number;
    }
    
    @Injectable()
    export class FirebaseService {
      postsRef: AngularFirestoreCollection<Post>;
    
      constructor(private db: AngularFirestore) { }
    
      getPosts() {
        this.postsRef = this.db.collection('/posts') as AngularFirestoreCollection<Post>;
        return this.postsRef.valueChanges();
      }
    }
    

    In your component:

    import { Observable } from 'rxjs';
    import { FirebaseService, Post } from '....';
    
    @Component({ ... })
    export class MyComponent implements OnInit {
      posts$: Observable<Post[]>;
    
      constructor(private firebaseService: FirebaseService) { }
    
      ngOnInit() {
        this.posts$ = this.firebaseService.getPosts();
      }
    }
    

    In your template:

    <mat-grid-tile *ngFor="let post of posts$ | async">
      ...
    </mat-grid-list>