Getting back to learning Angular and extending the Tour of Heroes tutorial. I have a shared data service that loads data into a BehaviorSubject. The issue is that when I try to iterate the data with *ngFor, I get the "cannot find a differ supporting object" error. From all the other questions, I get that it is trying to bind to an object and not an Array, but for the life of me I cannot figure out why. Or what object needs to get converted into an Array.
I am using Angular 5.0.5. Interestingly enough, this was working with Angular 4 but apparently I have broken something in the upgrade.
Any thoughts on what I did wrong? Besides everything. :D lol
This is my service
import { Injectable, EventEmitter } from '@angular/core';
import { HttpHeaders, HttpClient } from '@angular/common/http';
import { Subject } from 'rxjs/Subject';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { Hero } from '../models/hero';
@Injectable()
export class HeroService {
private heroesUrl = 'api/heroes';
private headers = new HttpHeaders({'Content-Type': 'application/json'});
private loadingSubject = new BehaviorSubject<boolean>(false);
private dataSubject = new BehaviorSubject<Hero[]>([]);
private http: HttpClient;
constructor(http: HttpClient) {
this.http = http;
}
public getLoadingStream(): Observable<boolean> {
return this.loadingSubject.asObservable();
}
public getDataStream(): Observable<Hero[]> {
return this.dataSubject.asObservable();
}
public load(): void {
this.loadingSubject.next(true);
this.http.get<Hero[]>(this.heroesUrl).subscribe(data => {
this.dataSubject.next(data);
this.loadingSubject.next(false);
});
}
public search(term: string): void {
this.loadingSubject.next(true);
this.http
.get<Hero[]>(`${this.heroesUrl}/?name=${term}`)
.subscribe(data => {
this.dataSubject.next(data);
this.loadingSubject.next(false);
});
}
}
List Component
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { HeroService } from '../../services/hero.service';
import { Hero } from '../../models/hero';
@Component({
selector: 'hero-list',
templateUrl: './hero-list.component.html',
styleUrls: ['./hero-list.component.css']
})
export class HeroListComponent implements OnInit {
private dataSubscription: Subscription;
private loadingSubscription: Subscription;
private heroService: HeroService;
heroes: Hero[] = [];
isLoading: boolean;
@Output()
public onHeroSelected = new EventEmitter<Hero>();
constructor(heroService: HeroService) {
this.heroService = heroService;
}
ngOnInit() {
this.loadingSubscription = this.heroService.getLoadingStream()
.subscribe(loading => {
this.isLoading = loading;
});
this.dataSubscription = this.heroService.getDataStream()
.subscribe(data => {
this.heroes = data;
});
}
ngOnDestory() {
this.dataSubscription.unsubscribe();
this.loadingSubscription.unsubscribe();
}
onSelect(hero: Hero) {
this.onHeroSelected.emit(hero);
}
}
List template
<div *ngIf="isLoading">Loading ...</div>
<div *ngIf="!isLoading" class="ui relaxed divided list">
<div *ngFor="let hero of heroes" class="item" (click)="onSelect(hero)">
<span class="ui blue circular label">{{ hero.id }}</span>
<div class="content">
<a class="header">{{ hero.name }}</a>
<div class="description">...</div>
</div>
</div>
</div>
isLoadingis false it will try to display and iterate over your array. But in that moment, your array isn't set because it's async. try to update from*ngIf=!isLoadingto*ngIf=!isLoading && heroes > 0- shepluthis.dataSubject.next(data);byconsole.log(data); this.dataSubject.next(data);. What is being logged to the console? Is it really an array? - JB Nizetconsole.log(JSON.stringify(data)). - JB Nizet