Razvan Zamfir
Razvan Zamfir

Reputation: 4712

Angular 9 application "NgFor only supports binding to Iterables such as Arrays" error

I am working on a Contacts app with Angular 9. I get a list of contacts via the following service:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import { Contact } from '../models/Contact';

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json'
  })
}

@Injectable({
  providedIn: 'root'
})

export class ContactsListService {

  contactsUrl = 'https://randomuser.me/api/?&results=500&inc=name,location,email,cell,picture';

  constructor(private http:HttpClient) { }

  getContcts():Observable<Contact[]> {
    return this.http.get<Contact[]>(`${this.contactsUrl}`);
  }

}

The Contacts List component is as follows:

import { Component, OnInit } from '@angular/core';
import { from } from 'rxjs';
import { ContactsListService } from '../../services/contacts-list.service';
import { Contact } from '../../models/Contact';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
})

export class ListComponent implements OnInit {

  contactsList:Contact[];

  constructor(private ContactsListService:ContactsListService) { }

  ngOnInit(): void  {
      this.ContactsListService.getContcts().subscribe(contactsList => {
        this.contactsList = contactsList;
      });

  }
}

Trying to iterate the contacts list this way

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

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

That very likely means contactsList is not an array.

What am I doing wrong?

Upvotes: 0

Views: 1114

Answers (2)

Barremian
Barremian

Reputation: 31135

The URL is returning an object of the form

{
  results: [],
  info: {}
}

Use RxJS map to map the results obtained from the API.

Service

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import { pipe } from 'rxjs';
import { map } from 'rxjs/operators';

import { Contact } from '../models/Contact';

export class ContactsListService {
  contactsUrl = 'https://randomuser.me/api/?&results=500&inc=name,location,email,cell,picture';

  constructor(private http:HttpClient) { }

  getContcts():Observable<Contact[]> {
    return this.http.get<Contact[]>(`${this.contactsUrl}`)
      .pipe(map(response => response['results']));
  }
}

Assign empty array to contactsList on declaration.

Component

import { Component, OnInit } from '@angular/core';

import { from } from 'rxjs';

import { ContactsListService } from '../../services/contacts-list.service';
import { Contact } from '../../models/Contact';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
})

export class ListComponent implements OnInit {
  contactsList: Contact[] = []; // assign empty array

  constructor(private ContactsListService:ContactsListService) { }

  ngOnInit(): void  {
    this.ContactsListService.getContcts().subscribe(
      contactsList => { this.contactsList = contactsList; },
      error => { // always good practice to handle error when subscribing to HTTP observables }
    );
  }
}

Upvotes: 3

IAfanasov
IAfanasov

Reputation: 4993

Seems like the endpoint 'https://randomuser.me/api/?&results=500&inc=name,location,email,cell,picture' does not return an array. To check it you may open it in the browser, or check in the network tab of the chrome developers tools while your app running

Upvotes: 0

Related Questions