Jason
Jason

Reputation: 334

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

I have a component loading a local json file (contains only name) from assets folder.Since HttpClient takes care of formatting data as json, so didn't use map here.

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'

 export type cont= {name: string};

@Component({
  selector: 'my-app',
  template: `
    1st List
    <ul>
      <li *ngFor="let contact of contacts | async">{{contact.name}}</li>
    </ul>
    2st List
    <ul>
      <li *ngFor="let contact of contacts2 | async">{{contact.name}}</li>
    </ul>
    `
})
export class AppComponent {;
  contacts: Observable<cont[]>;
  contacts2: Observable<cont[]>;
  constructor (http: HttpClient) {
    this.contacts = http.get<cont[]>('http://localhost:4200/assets/contacts.json');               
    setTimeout(() => this.contacts2 = this.contacts, 500);
  }
}

Contacts.json

 {
"items": [
  { "name": "Mark Twain" },
  { "name": "Sherlock Holmes" }
]

}

Data can be fetched on browser via http://localhost:4200/assets/contacts.json but When I tried to render the same via async pipe I get this error below.Tried many ways to convert observable to Observable<[]> but none passed this scenario. What's wrong here. how json data over httpclient can be converted to Array ? (tried to follow similar posts but none helped)?

Error

Upvotes: 4

Views: 11380

Answers (2)

Andriy
Andriy

Reputation: 15442

You are trying to iterate over an object:

{
  "items": [
    { "name": "Mark Twain" },
    { "name": "Sherlock Holmes" }
  ]
}

You should iterate over contacts.items

<ul>
  <li *ngFor="let contact of (contacts | async)?.items">{{contact.name}}</li>
</ul>

or just use observable map:

const url = 'http://localhost:4200/assets/contacts.json';
this.contacts = http.get<cont[]>(url).pipe(map(data => data.items)); 

ANOTHER SOLUTION using Symbol.iterator

I am adding it just because it is interesting (in my opinion) approach although not practical in this case.

I can leave iteration over an asynchronous object:

<ul>
  <li *ngFor="let contact of contacts | async">{{contact.name}}</li>
</ul>

but I will convert this object to iterable by adding iterable protocol:

const url = 'http://localhost:4200/assets/contacts.json';
this.contacts = http.get<cont[]>(url).pipe(
  map((data: any) => {
    data[Symbol.iterator] = () => data.items[Symbol.iterator]();
    return data;
  })
);

The line

data[Symbol.iterator] = () => data.items[Symbol.iterator]()

redirects iteration over the object (which is impossible by default) to iteration over its items array.

I created a STACKBLITZ demonstrating this

Upvotes: 7

ankit chauhan
ankit chauhan

Reputation: 37

this will surely work just define varible correctly

this happens when data json you have in the variable is not in the array format.define variable like this

 contacts:[] and store the json from api like this:-
    response = http.get<cont[]>('http://localhost:4200/assets/contacts.json'); 
    this.contacts= response.items //array start from items key

    <ul>
      <li *ngFor="let contact of contacts">{{contact.name}}</li>
    </ul>

Upvotes: 0

Related Questions