8

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 Answers 2

14

A good practice is naming the observables with a $ suffix, let's 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>
Sign up to request clarification or add additional context in comments.

Comments

11

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.