Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 *ngFor: "Cannot read property '0' of undefined"

I trying to get data from a JSON file to build a form.

Here is a portion of my template:

  <div class="form-group">
    <label for="power">Power</label>
    <select class="form-control" id="power" required>
      <option *ngFor="let p of heroes" [value]="p.level">{{p.level}}</option>
    </select>
  </div>

Here is part of the remote JSON file:

{
    "data": [
        {
            "level": "newbie",
            "places": [
                {
                    "place": "earth",
                    "categories": [
                        {
                            "category": "human",
                            "values": [
                                ...

It works with no problem and i get newbie and other choices in the select menu. But i want to loop on places, so i edit the html template in this way:

  <div class="form-group">
    <label for="power">Power</label>
    <select class="form-control" id="power" required>
      <option *ngFor="let p of heroes[0].places" [value]="p.place">{{p.place}}</option>
    </select>
  </div>

Here is the service that i use to grab data from JSON file:

@Injectable()
export class HeroService {
    private url = 'app/mockups/heroes.json';

    constructor(private http: Http) { }

    getHeroes(): Promise<Hero[]> {
        return this.http.get(this.url)
            .toPromise()
            .then(response => response.json().data as Hero[])
            .catch();
    }
}

and here is the hero.component:

export class HeroComponent implements OnInit {
    heroes: Hero[];

    constructor(private heroService: HeroService) { }

    ngOnInit():void {
        this.getHeroes();
}

    getHeroes(): void {
        this.heroService.getHeroes().then(heroes => this.heroes = heroes);
  }

But i get "Cannot read property '0' of undefined" error.

Why?

like image 599
smartmouse Avatar asked Sep 06 '16 08:09

smartmouse


3 Answers

I guess what you want is

*ngFor="let p of heroes?.data"

because heroes seems to be an object, and ngFor can only iterate array. The level property also is in an array item.

like image 87
Günter Zöchbauer Avatar answered Oct 28 '22 07:10

Günter Zöchbauer


I figured out the problem. I got this error because I'm fetching data asynchronously and when Angular tries to resolve bindings the first time data is still null therefore heroes[0] fails.

So I solved the problem initializing heroes array and using the "Elvis operator":

heroes: Hero[]; instead of heroes: Hero[] = []; in the component.

heroes[0]?.places instead of heroes[0].places in the html template.

like image 38
smartmouse Avatar answered Oct 28 '22 05:10

smartmouse


Alternative to @Gunter Zochbauer's solution, you can declare heroesas Array property of suitable type. If you have any class with all attributes of heroes you declare heroes property as:

heroes:Array<Heroes> //Assuming class name is Heroes

And initialize it in constructor as follows:

constructor(){
...    
this.heroes=new Array<Heroes>();
}

And in your ngFor loop simply access class attributes as follows:

<option *ngFor="let p of heroes" [value]="p.place">{{p.place}}</option>

Hope it helps.

like image 29
Karan Desai Avatar answered Oct 28 '22 07:10

Karan Desai