3

I'm pretty new to both Ionic and Angular so please excuse if this question doesn't make much sense.

Platform : Ionic 3.

I have a provider : rest.ts as follows

getLights() {
  return new Promise(resolve => {
    this.http.get(this.apiUrl+'/lights').subscribe(data => {
      resolve(data);
    }, err => {
      console.log(err);
    });
  });
}

This returns the following data (sensitive info removed)

{
    "1": {
        "state": {
            "on": false,
            "bri": 98,
            "alert": "none",
            "reachable": true
        },
        "swupdate": {
            "state": "noupdates",
            "lastinstall": null
        },
        "type": "Dimmable light",
        "name": "Lion",
        "modelid": "LWB010",
        "manufacturername": "Philips",
    },
    "2": {
        "state": {
            "on": true,
            "bri": 100,
            "alert": "none",
            "reachable": true
        },
        "swupdate": {
            "state": "noupdates",
            "lastinstall": null
        },
        "type": "Dimmable light",
        "name": "Brutus",
        "modelid": "LWB010",
        "manufacturername": "Philips",
    },
    "3": {
        "state": {
            "on": true,
            "bri": 254,
            "hue": 8418,
            "sat": 140,
            "effect": "none",
            "xy": [
                0.4573,
                0.41
            ],
            "ct": 366,
            "alert": "none",
            "colormode": "ct",
            "reachable": true
        },
        "swupdate": {
            "state": "noupdates",
            "lastinstall": null
        },
        "type": "Extended color light",
        "name": "Hue lightstrip plus 1",
        "modelid": "LST002",
        "manufacturername": "Philips"
    }
}

An ionic page 'list' made up of : list.ts

export class ListPage {

  lights: any;
  icons: string[];

  constructor(public navCtrl: NavController,
              public navParams: NavParams,
              public restProvider: RestProvider) {

    this.getLights();
  }

  getLights() {
    this.restProvider.getLights()
    .then(data => {
      this.lights = data;
      console.log(this.lights);
    });
  }

}

and list.html :

<ion-content>
  <ion-list inset>
    <ion-item *ngFor="let light of lights">
      <h2>{{light.name}}</h2>
      <p>{{light.type}}</p>
    </ion-item>
  </ion-list>
</ion-content>

Although the console log statement displays the JSON correctly, I can't iterate it using ngFor in list.html due to the following error.

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

I realise I need to convert the JSON into a javascript array, but not sure where or how to do this. Any pointers would be very much appreciated.

Robin M
  • 664
  • 6
  • 18

2 Answers2

0

There are a variety of ways you can convert an Array-like object to an Array depending on how it's composed. In this case you could do it pretty easily with:

data => resolve(Object.keys(data).map(key => data[key]))

This will resolve with an array of the objects from the promise and should work with your component.

I'll point out that you don't have to use your own Promise, you can use the Observable method toPromise:

getLights() {
  return this.http.get(`${this.apiUrl}/lights`).map(data =>
    Object.keys(data).map(key => data[key])
  ).toPromise();
}

In fact you don't even need to use a promise and you can just subscribe to getLights instead.

this.restProvider.getLights().subscribe(data => {
  this.lights = data;
});

NOTE:

If this is not using Angular 5 I think you need to do:

this.http.get(url).map(response => response.json())

in the Observable chain as well.

Explosion Pills
  • 188,624
  • 52
  • 326
  • 405
0

Thank you for the response above. Very helpful, but unfortunately I couldn't get any of it to work as I expected ... down to my inexperience likely.

I'm still unable to get round the issue of the JSON containing numbers as keys and being able to iterate over the lights....

However, I was able to cast the response of one light (altering my API call to get light '1') :

{
"state": {
   "on": false,
   "bri": 98,
   "alert": "none",
   "reachable": true
},
"swupdate": {
  "state": "noupdates",
  "lastinstall": null
},
"type": "Dimmable light",
"name": "Lion",
"modelid": "LWB010",
"manufacturername": "Philips",
}

using interfaces :

export interface State {

  on: boolean;
  bri: number;
  alert: string;
  reachable: boolean;
}

export interface Swupdate {
  state: string;
  lastinstall?: any;
}

export interface Light {
  state: State;
  swupdate: Swupdate;
  type: string;
  name: string;
  modelid: string;
  manufacturername: string;
  uniqueid: string;
  swversion: string;
  swconfigid: string;
  productid: string;
}

Then using the HttpClient call to cast the object as follows :

this.http.get<Light>(this.apiUrl+'/lights/1').subscribe(data => {
        console.log(data.state.bri);
})

If anyone can detail the structure of an interface that might successfully cast a list of lights where the key is a number, that would be ideal ...

Robin M
  • 664
  • 6
  • 18