2
votes

I am trying to make use of the async pipe to display a table in Angular, but am getting the error: "...Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays."

My code:

<div *ngIf="studentList | async; else loading">
  <table>
    <tr>
      <th>ID</th>
      <th>First Name</th>
      <th>Last Name</th>
    </tr>
    <tr *ngFor="let student of studentList">
      <td>{{student.id}}</td>
      <td>{{student.first_name}}</td>
      <td>{{student.last_name}}</td>
    </tr>
  </table>
  {{studentList | json}}
</div>

<ng-template #loading>
  <div>Loading ...</div>
</ng-template>

The back-end is returning an Observable<Student[]>

If I replace the ngFor with the below, it works but that means two HTTP.get calls are made, which I don't want:

<tr *ngFor="let student of (studentList | async)">

Is there a way for me to reuse the value of studentList from the ngIf within the ngFor, thus only making one request to my back-end?

2

2 Answers

9
votes

A good practice is naming the observables with a $ sufix, lets say studentList$ is of type Observable<Student[]>. You can use the async with as to get the studentList which is of type Student[] :

<div *ngIf="studentList$ | async as studentList; else loading">
  <table>
    <tr>
      <th>ID</th>
      <th>First Name</th>
      <th>Last Name</th>
    </tr>
    <tr *ngFor="let student of studentList">
      <td>{{student.id}}</td>
      <td>{{student.first_name}}</td>
      <td>{{student.last_name}}</td>
    </tr>
  </table>
</div>
5
votes

Apart from an amazing and simple answer by @ibenjelloun, I also wanted to add another approach by using templateOutlet

Pass on the evaluated Observable to an ng-template and use the template by templateOutlet. You can have something like this in your template:

<ng-container
    *ngTemplateOutlet="asyncTemplate;context:{listEvaluated: studentList | async}">
</ng-container>


<ng-template #asyncTemplate let-inputList="listEvaluated">
    <div *ngIf="inputList; else loading">
      <table>
        <tr>
          <th>ID</th>
          <th>First Name</th>
          <th>Last Name</th>
        </tr>
        <tr *ngFor="let student of inputList">
          <td>{{student.id}}</td>
          <td>{{student.first_name}}</td>
          <td>{{student.last_name}}</td>
        </tr>
      </table>
    </div>
</ng-template>

Please note: The | pipe operator in the context object works as a pipe and not as a bitwise operator.