0
votes

I'm having trouble figuring out why I'm getting this Angular error: "Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays."

My guess is that my service's returned observable is returning an object for some reason, when I need it to be an array--I just don't know how it's doing so. Any help would be much appreciated! I saw several other questions people had asked about the same error, but none of the solutions I saw worked for me.

app.component:

export class AppComponent implements OnInit {
  constructor(private playersService: PlayersService) { }

  teams: string[];
  year: number = 2019; 
  selectedYear: number;
  years: number[] = this.getYears(1900);

  ngOnInit() {
    this.refreshTeams(this.year);
  }

  refreshTeams(year: number) {
    this.teams = this.playersService.fetchTeams(this.year).subscribe(data => { this.teams = data });
  }

  onYearChange() : void {
    this.year = this.selectedYear;
    this.refreshTeams(this.year);
  }

  getYears(startYear) : number[] {
    var currentYear = new Date().getFullYear()
    var years = [];
    for (var x = currentYear; x >= startYear; x--) {
      years.push(x);
    }
    return years;
  }
}

players.service:

export class PlayersService {

    constructor(private http: HttpClient) { }

    returnedTeams = [];

    fetchTeams(year: number) : any {

        var queryString = `http://lookup-service-prod.mlb.com/json/named.team_all_season.bam?sport_code='mlb'&all_star_sw='N'&sort_order=name_asc&season='${year}'`;

        return this.http.get(queryString)
            .pipe(
                map(data => {
                    const teamArray = [];
                    var rows = data['team_all_season'].queryResults.row;
                    for (var item in rows) {
                        teamArray.push(rows[item].name_display_full);
                    }
                    return teamArray;
                })
            )
    }
}

app.component.html:

<button (click)="fetchTeams()">Fetch</button>

<select [(ngModel)]="selectedYear" (change)="onYearChange($event)">
  <option *ngFor = "let year of years" value="{{ year }}">{{ year }}</option>
</select>

<select>
  <option *ngFor="let team of teams">
    {{ team }}
  </option>
</select>
2

2 Answers

1
votes

The problem is that you assign the result of the subscribe call to the teams property:

this.teams = this.playersService.fetchTeams(this.year).subscribe(...);

Since that call returns a Subscription, it is not compatible with ngFor. The error probably occurs before the Observable has emitted its value. To avoid it, you should set the teams property only inside of the callback:

this.playersService.fetchTeams(this.year).subscribe(data => {
  this.teams = data;
});
1
votes

You could use Angular async pipe and you do not have to subscribe manually.

async also unsubscribes the observable automatically.

<select>
  <option *ngFor="let team of (teams$ | async)">
    {{ team }}
  </option>
</select>

comp.ts

teams$: Observable<string[]>;
refreshTeams(year: number) {
    this.teams$ = this.playersService.fetchTeams(this.year);
}

Demo