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

11,867

Solution 1

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.

Solution 2

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.

Solution 3

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.

Share:
11,867
smartmouse
Author by

smartmouse

Considerable experience in web applications development, both as front-end developer and as CMS webmaster. Bitcoin and blockchain enthusiast as writer, speaker and developer of personal projects. An effective communicator with good leadership and analytical skills. Seeking new challenges and responsibilities to progress career. Spare time is for reading news, traveling and working on new ideas...

Updated on June 13, 2022

Comments

  • smartmouse
    smartmouse almost 2 years

    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?

  • smartmouse
    smartmouse over 7 years
    Yes, that heroes in the html template loops on data array. Now i want to loop on data[0].places array, where [0] is the first object of data array.
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    That wasn't obvious from your question. I guess the error is caused by the code that assigns the json to heroes.
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    According to your update "Yes, that heroes in the html template loops on data array. " doesn't seem to be true. Did you try the code in my answer?
  • smartmouse
    smartmouse over 7 years
    Yes, i loop on data array because i can see items in the output. I get it from service (.then(response => response.json().data as Hero[])). For this reason you code doesn't work.
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    Then the JSON you posed in the question is not exactly what you assign to heroes
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    Have you tried [value]="p?.place" instead of [value]="p.place"?
  • Varun Sharma
    Varun Sharma over 6 years
  • P. Nabin
    P. Nabin over 6 years
    @smartmouse heroes: Hero[]; instead of heroes: Hero[]; in the component. same value instead of same value, I am confused.
  • Liam
    Liam about 5 years
    Isn't this exactly what Gunter said?