1

I am trying to iterate over an Array property of an object as below:

<p>
  Editing Course : {{course?.name}}<br/>
  Course Outward Par : {{course?.getOutwardPar('MEDAL')}}<br/>
  Course Outward Yards : {{course?.getOutwardYards('MEDAL')}}
</p>

<div class="container">
  <h1>Course Form</h1>
  <form (ngSubmit)="onSubmit()" #heroForm="ngForm">
    <div class="form-group">
      <table>
        <tr>
          <td><label for="name">Name</label></td>
          <td><input type="text" class="form-control" id="name" name="name" required
                     [(ngModel)]="course && course.name">
          </td>
        </tr>

        <tr>
          <input type="number" class="form-control" id="hole1" name="hole1" required
                 [(ngModel)]="course && course.holes[1].tees['MEDAL'].par">
        </tr>

        <!-- 
             --- BROKEN HERE---
         -->
        <tr *ngFor="let hole of course?.holes">
          <td>{{hole.name}}</td>
        </tr>

      </table>
    </div>

    <button type="submit" class="btn btn-default">Submit</button>
  </form>
</div>

Everything prior to the *ngFor works as expected including the below so the property holes of course can surely be considered to be an array. No?

<input type="number" class="form-control" id="hole1" name="hole1" required
                     [(ngModel)]="course && course.holes[1].tees['MEDAL'].par">

The ngFor triggers the error:

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

There is some dicusssion here https://github.com/angular/angular/issues/6392 on the subject. The post by robwormald suggests that the ? opertaor can be used in such cases.

Alan Hay
  • 22,665
  • 4
  • 56
  • 110
  • why is there a ? in let hole of course?.holes – Mitesh Pant Jan 19 '17 at 19:12
  • @MiteshPant: Safe operator for data that is received async, so app won't throw error for loading view before data has arrived :) – AT82 Jan 19 '17 at 19:15
  • http://www.syntaxsuccess.com/viewarticle/elvis-operator-in-angular-2.0 – Alan Hay Jan 19 '17 at 19:15
  • @AlanHay yeah, you should be able to use the elvis-operator in this case. How does your data look like.... is `holes` in fact an array? – AT82 Jan 19 '17 at 19:20
  • 1
    The input above the loop works without any issues. Course is a TS class wrapping some JSON. I can iterate the array 'holes' in the TS class without any issues to provide calculated values. Updating a value in the standalone input correctly updates the corresponding model value and the calculated value is updated as expected. – Alan Hay Jan 19 '17 at 19:25
  • I think my array is what would be termed an associative array in Javascript. It appears that may be the issue. The source Json is the serialization of a Java map. – Alan Hay Jan 19 '17 at 19:57

1 Answers1

1

Okay it appears Angular 2 does not support using associative arrays / Dictionaries in *ngFor

e.g. JSON with the structure below.

See for example:

Iterate over TypeScript Dictionary in Angular 2

I could get this to work by simply creating a keys variable in the Component and iterating that. Other solutions propose using a Directive but that's for later.

e.g.

keys: string [];
this.keys = Object.keys(this.course.holes);

and iterating the keys

<tr *ngFor="let key of keys">
  <td>{{key}}</td>
  <td>{{course.holes[key].name}}</td>
  <td>
    <input type="number" class="form-control" id="hole{{key}}" name="holePar{{key}}"
           [(ngModel)]="course && course.holes[key].tees['MEDAL'].par"/>
  </td>
  <td>

        <input type="number" class="form-control" id="hole{{key}}" name="holeLength{{key}}"
               [(ngModel)]="course && course.holes[key].tees['MEDAL'].length"/>
      </td>
    </tr>

Source JSON:

{
    "name": "Ny Course",
    "courseTeeSets": [],
    "holes": {
        "1": {
            "id": 1,
            "number": 1,
            "name": "Hole Number 1",
            "tees": {
                "MEDAL": {
                    "id": 3,
                    "teeType": "MEDAL",
                    "length": 100,
                    "strokeIndex": 15,
                    "par": 8
                },
                "MENS": {
                    "id": 1,
                    "teeType": "MENS",
                    "length": 509,
                    "strokeIndex": 15,
                    "par": 5
                },
                "LADIES": {
                    "id": 2,
                    "teeType": "LADIES",
                    "length": 489,
                    "strokeIndex": 15,
                    "par": 5
                }
            }
        },
        "2": {
            "id": 2,
            "number": 2,
            "name": "Hole Number 2",
            "tees": {
                "MEDAL": {
                    "id": 4,
                    "teeType": "MEDAL",
                    "length": 110,
                    "strokeIndex": 9,
                    "par": 8
                },
                "MENS": {
                    "id": 6,
                    "teeType": "MENS",
                    "length": 191,
                    "strokeIndex": 9,
                    "par": 4
                },
                "LADIES": {
                    "id": 5,
                    "teeType": "LADIES",
                    "length": 171,
                    "strokeIndex": 9,
                    "par": 4
                }
            }
        }
    }
}
Community
  • 1
  • 1
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
  • Well, this is why I asked if `holes` is an array... ;) But good that you figured out the solution! :) – AT82 Jan 19 '17 at 21:42