0

I am new to Angular 2 and I am trying to create service which send get request and gets json. And bind those result from json to array of angular classes. But when there is trouble and something went wrong. I followed documentation on angular.io and did everything like there. Through debugger I found that when i write

return body.data

after that object that returns is undefined.

I got next error:

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

Please help me with this issue.

Json data:

[{"categoryId":1,"categoryName":"cpu"},{"categoryId":2,"categoryName":"gpu"},{"categoryId":3,"categoryName":"motherboard"},{"categoryId":4,"categoryName":"phone"},{"categoryId":5,"categoryName":"hdd"},{"categoryId":6,"categoryName":"ssd"},{"categoryId":7,"categoryName":"ram"},{"categoryId":8,"categoryName":"rom"}]

Entity class:

export class Category {
  constructor(public categoryId: number, public categoryName: string) {}
}

Service class:

@Injectable()
export class CategoryService {
  private currentUrl = 'http://localhost:8081/emusicshop/api/categories';

  constructor (private http: Http) {}

  getCategories(): Observable<Category[]> {
    return this.http.get(this.currentUrl)
      .map(this.extractData)
      .catch(this.handleError);
  }

  private extractData(res: Response) {
    let body = res.json();
    return body.data || { };
  }
  private handleError (error: Response | any) {
    // In a real world app, you might use a remote logging infrastructure
    let errMsg: string;
    if (error instanceof Response) {
      const body = error.json() || '';
      const err = body.error || JSON.stringify(body);
      errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
    } else {
      errMsg = error.message ? error.message : error.toString();
    }
    console.error(errMsg);
    return Observable.throw(errMsg);
  }
}

Component:

Component
export class CategoryComponent implements OnInit {

  allCategories: Category[];

  constructor(private service: CategoryService) { }

  getCategories(): void {
    this.service.getCategories().subscribe(
      categories => this.allCategories = categories);
  }

  ngOnInit() {
    this.getCategories();
  }

}

HTML file:

<ul>
  <li *ngFor="let categ of allCategories">
    Id : {{categ.id}}
    Name : {{categ.name}}
  </li>
</ul>
  • If you are using `*ngFor` in your template, it needs to run on an array. `console.log(categories)` inside the subscribe to see what it really is – eko Apr 09 '17 at 14:21
  • @echonax I know, but I got trouble while binding those json file to my allCategories property and those allCategories property is an array – Maksym Vasylenko Apr 09 '17 at 14:22
  • Inside `extractData` do a `console.log(res);`, what's the result? – eko Apr 09 '17 at 14:24
  • `body.data || { }` convert it to `body.data || []` and make sure your `body.data` must be an array. – Babar Hussain Apr 09 '17 at 14:26
  • can you show us the json data. i think you are not able to match the json data with you category interface. – Niraj Apr 09 '17 at 14:27
  • @Niraj I add json data into question – Maksym Vasylenko Apr 09 '17 at 14:32
  • @MaksymVasylenko sorry my bad i didnt see it. I think you should try changing body.data to body only. – Niraj Apr 09 '17 at 14:34
  • @echonax Response {_body: "[{"categoryId":1,"categoryName":"cpu"},{"categoryI…me":"ram"},{"categoryId":8,"categoryName":"rom"}]", status: 200, ok: true, statusText: "OK", headers: Headers…} headers : Headers ok : true status : 200 statusText : "OK" type : 2 url : "http://localhost:8081/emusicshop/api/categories" _body : "[{"categoryId":1,"categoryName":"cpu"},{"categoryId":2,"categoryName":"gpu"},{"categoryId":3,"categoryName":"motherboard"},{"categoryId":4,"categoryName":"phone"},{"categoryId":5,"categoryName":"hdd"},{"categoryId":6,"categoryName":"ssd"},......... – Maksym Vasylenko Apr 09 '17 at 14:35
  • @MaksymVasylenko please debug your code carefully. I don't see a `data` field in it. So why are you selecting `body.data`? – eko Apr 09 '17 at 14:36
  • @BabarBilal i tryied console.log(body.data) and output was "undefined" – Maksym Vasylenko Apr 09 '17 at 14:37
  • @echonax cause in angular tutorial it was like i wrote "body.data" – Maksym Vasylenko Apr 09 '17 at 14:38
  • @MaksymVasylenko you should console only body not body.data – Niraj Apr 09 '17 at 14:39
  • @MaksymVasylenko in the tutorial the server sends an object with a `data` field that's why they select `data`. – eko Apr 09 '17 at 14:39
  • @echonax when i just return "body" almost everything is ok but hen my allCategories property is undefined(( – Maksym Vasylenko Apr 09 '17 at 14:41
  • @MaksymVasylenko where is it undefined? – eko Apr 09 '17 at 14:41

1 Answers1

1

Your response object does not have a data field. It should be more like this:

private extractData(res: Response) {
    let body = res.json();
    return body || []; //<-- return an empty array instead of an object so *ngFor won't complain about iteration
}

And try the safe navigation operator on your template ?

<ul>
  <li *ngFor="let categ of allCategories">
    Id : {{categ?.categoryId}}
    Name : {{categ?.categoryName}}
  </li>
</ul>
eko
  • 39,722
  • 10
  • 72
  • 98
  • Thank you it helped a little bit. But now the problem is that how to bind those json to my allCategories property. Now it works and loop through ngFor but no data from element displayed. For example i have 8 elements there and i got outrput: Id : Name : Id : Name : Id : Name : Id : Name : Id : Name : Id : Name : Id : Name : Id : Name : Without any properties – Maksym Vasylenko Apr 09 '17 at 14:47
  • The same result – Maksym Vasylenko Apr 09 '17 at 14:51
  • @MaksymVasylenko what does `this.service.getCategories().subscribe( categories =>{ console.log(categories); this.allCategories = categories});` log? – eko Apr 09 '17 at 14:52
  • [Object, Object, Object, Object, Object, Object, Object, Object] 0 : Object categoryId : 1 categoryName : "cpu" __proto__ : Object 1 : Object categoryId : 2 categoryName : "gpu" __proto__ : Object 2 : Object categoryId : 3 categoryName : "motherboard" __proto__ : Object etc – Maksym Vasylenko Apr 09 '17 at 14:54
  • @MaksymVasylenko is this even an array? – eko Apr 09 '17 at 14:55
  • @MaksymVasylenko ah it slipped my attention too..I edited mine like that. You can self answer your question to close the issue if you want – eko Apr 09 '17 at 15:02
  • and when i try for example to create const superCategory = this.allCategories[1]; after this.getCategories(); in ngOnInit method i get an errror Uncaught (in promise): TypeError: Cannot read property '1' of undefined TypeError: Cannot read property '1' of undefined – Maksym Vasylenko Apr 09 '17 at 15:03
  • and on the end of ngOnInit method i watch this.allCategories property on google chrome debugger and it is written there that this.allCategories: undefined – Maksym Vasylenko Apr 09 '17 at 15:12
  • @MaksymVasylenko it's because `getCategories` is an async method. Take a look at: http://stackoverflow.com/questions/43055706/how-do-i-return-the-response-from-an-observable-http-async-call-in-angular2 – eko Apr 09 '17 at 15:13
  • 1
    Maksym, please accept the answer if it solved your issue (by clicking the grey tick under the voting of this answer). This way, the question is marked as solved :) – AT82 Apr 09 '17 at 16:58