Hamdi Gatri
Hamdi Gatri

Reputation: 101

Angular2 : TypeError 'this.' is undefined

I wrote a function that gets and shows the "best player" from my array of objects (the player who has most likes). The function works fine and shows me what I want, but the browser shows me errors in the console, and the routing in my app is blocked (I can't navigate between components using routes)

this is my DashboardComponent Class

export class DashboardComponent implements OnInit {
  bestPlayer:Player;
  data:Player[]=[];
  max:number =0;
  constructor(private playerService : PlayerService ,private router: Router) { }

  ngOnInit() { 
    this.playerService.getData().then(data => {
      this.data = data;
      for (var i = 0; i <= this.data.length; i++) {
        if (this.data[i].likes>this.max) {
          this.max=this.data[i].likes;
          this.bestPlayer=this.data[i];
        }
      }
    });  
  }

  viewDetails(bestPlayer: Player):void {
    this.router.navigate(['/detail',this.bestPlayer.id]);
  }
}

This is my service:

import {Player} from './player';
import {PlayersData} from './muck-players';
import { Injectable } from '@angular/core';

@Injectable()
export class PlayerService {
  players:any;
  data:any;
  Player:Player;

  getData():Promise <Player[]> {
    return Promise.resolve(PlayersData);
  }
}

when I run the app the browser show me those errors :

TypeError: this.data[i] is undefined Error: Uncaught (in promise): Error: Cannot activate an already activated outlet Error: Uncaught (in promise): Error: Cannot activate an already activated outlet

when I delete whats in ngOninit() and viewDetails() function, routing starts working again and browser doesn't show me errors.

Any help please !

Upvotes: 3

Views: 3904

Answers (3)

Hamed Baatour
Hamed Baatour

Reputation: 6932

I have fixed dozens of errors in the Plunker and added some missing routing features. The App is working just fine now please take a look at my forked Plunker over here

I fixed all the files paths and used in forEach method in your component just like this:

ngOnInit() { 
  this.playerService.getData().then(data => {
      data.forEach( (arrData) => {
      if (arrData.likes>this.max) {
      this.max=arrData.likes;
      this.bestPlayer=arrData;
      }
    }) 
  });
}

Demonstration:

enter image description here

Upvotes: 1

AVJT82
AVJT82

Reputation: 73357

As a sidenote, always when you provide a plunker, make sure it's a working one ;) When I got it working, there was only a couple of issue actually. There was nothing wrong with your routing. The first error you were getting

this.data[i] is undefined

is because of your for loop, you had marked that we should loop until i matches the length of the array (or equal). But we need to remember that the index of arrays start with 0. So the last iteration it was trying to read an index that was not present in your array. So you should add -1 to your for loop:

for (var i = 0; i <= this.data.length-1; i++) 

or do

for (var i = 0; i < this.data.length; i++)

When this was fixed, it produces new issues. Since data is coming async, so by the time the template is rendered, your variable bestPlayer is undefined. This can be fixed with safe navigation operator, or wrapping everything inside a div with the condition that bestPlayer has values. This need to be applied to both the detail page and the dashboard. With the dashboard:

<div *ngIf="bestPlayer">
  <!-- code here -->
</div>

And in the detail page the same but using player instead, as that is the variable you are using there.

As mentioned, you can also use the safe navigation operator.

These actually cleared the second error you also had.

Here's your fixed PLUNKER.

Upvotes: 3

Vivek Doshi
Vivek Doshi

Reputation: 58553

Hi the issue is with your service,

You are trying to loop through data that is not available,

You need to change the code , If you are using Observer as service then put you code inside .subscribe method,

If you are using promise then put your looping code inside .then() method.

Try to use this :

If you are returning promise from this.playerService.getData() this service

this.playerService.getData().then((data) => {
 this.data = data;
 for (var i = 0; i <= this.data.length; i++) {
    if (this.data[i].likes>this.max) {
      this.max=this.data[i].likes;
      this.bestPlayer=this.data[i];


  }
})

If you are returning observable from this.playerService.getData() this service

   this.playerService.getData().subscribe((data) => {
     this.data = data;
     for (var i = 0; i <= this.data.length; i++) {
        if (this.data[i].likes>this.max) {
          this.max=this.data[i].likes;
          this.bestPlayer=this.data[i];
      }
    })

Upvotes: 1

Related Questions