0
votes

With Book class defined as:

class Book {
   id: number;
   name: string;
   constructor(id: number, name: string) { 
     this.id = id;
     this.name = name;
  }
}

I go ahead and create Observable object books$:

const book1: Book = new Book(1, "Title 1");
const book2: Book = new Book(2, "Title 2");
function booklist(observer) {
  observer.next(book1);
  observer.next(book2);
  observer.complete();
  return {unsubscribe() {}};
}
this.books$ = new Observable(booklist);

Checking it with:

this.books$.subscribe(each => console.log(each));

prints:

Book {id: 1, name: "Title 1"}
Book {id: 2, name: "Title 2"} 

But using this observable with ngFor in the html template:

<ul>
  <li *ngFor="let book of books$ | async" >
    Id: {{book.id}}, Name: {{book.name}}
  </li>
</ul>

raises the ERROR:

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

How to solve this problem? Here is the link to Stackblitz project:

https://stackblitz.com/edit/angular-ivy-isebs7?file=src%2Fapp%2Fapp.component.ts

2

2 Answers

1
votes

Everything looks good except books$ represent a stream of objects, not arrays. So might have to use keyvalue pipe. Try the following

<ul>
  <ng-container *ngIf="(books$ | async) as books">
    <li *ngFor="let book of books | keyvalue" >
      {{book.key}}: {{book.value}}
    </li>
  </ng-container>
</ul>

Update: RxJS bufferCount operator

The above solution however prints only the current notification. If you wish to collect each notification and display them all, you could use RxJS bufferCount (or buffer if you don't exactly the amount of notifications) operator to collect all the notifications.

Controller

import { bufferCount } from 'rxjs/operators';  

ngOnInit(): void {
  ...
  this.books$ = <Observable<Book[]>>(new Observable(booklist).pipe(bufferCount(2)));
  this.books$.subscribe(each => console.log(each));
}

Template

<ul>
  <ng-container *ngFor="let books of books$ | async">
    <li *ngFor="let book of books | keyvalue" >
      {{book.key}}: {{book.value}}
    </li>
  </ng-container>
</ul>

I've modified your Stackblitz

0
votes

I would extract this behaviour into a new method:

getBooksArr$: Observable<Book[]> {
   return this.books$.pipe(map((book) => Object.values(book)));
}

In the html:

<ul>
  <li *ngFor="let book of getBooksArr$() | async" >
    Id: {{book.id}}, Name: {{book.name}}
  </li>
</ul>