Guillaume D
Guillaume D

Reputation: 185

Difficulties with undefined properties during variable initialization in callback

I Have an API rest Server in NodeJS with express Framework who communicate with my Front made with Angular7. The server store and send some score objects the model is the same front and back sided.

The problem is i'm facing issues with the following errors:

ERROR TypeError: Cannot set property 'userScore' of undefined
ERROR TypeError: Cannot read property 'highScore' of undefined
ERROR TypeError: Cannot set property 'highScore' of undefined

My component where the error appear:

import {AfterViewInit, Component, ElementRef, HostListener, OnInit, 
ViewChild} from '@angular/core';
import {ScoreService} from './services/score.service';
import {Score} from './models/score';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, AfterViewInit {
  contentScores: {
    highScore: number,
    userScore: Score
  };
  scroll: boolean;
  badgeHidden: boolean;
  private yOffSet: any;

  @ViewChild('navbar') navElement: ElementRef;

  constructor(private score: ScoreService) {
    this.scroll         = false;
    this.badgeHidden    = true;
  }

  ngOnInit(): void {
    this.score.getHighScore().subscribe( score => {
      console.log('Score value: ' + score.score);
      console.log('Object: ' + score);
      this.contentScores.highScore = score.score;
    });
    this.score.getScoreOnIp().subscribe( score => {
      console.log('Score value: ' + score.score);
      console.log('Object: ' + score);
      this.contentScores.userScore = score;
    });
  }

  ngAfterViewInit(): void {
    this.yOffSet = this.navElement.nativeElement.offsetTop;
  }

  onClickReset(): void {
    console.log('----------INFO------------');
    console.log(this.contentScores.highScore);
    console.log(this.contentScores.userScore.score);
    console.log(this.contentScores.userScore._id);
    console.log('--------------------------');
    this.score.deleteScore(this.contentScores.userScore._id);
  }

  @HostListener('window:scroll', ['$event'])
  onWindowScroll(): void {
    const currYOffset = window.pageYOffset;

    if (this.yOffSet < currYOffset) {
      this.scroll = true;
    } else {
      this.scroll = false;
    }
  }
}

and the location in the view when the variables are called:

<p class="dropdown-item-text">Le meilleur score enregistré est actuellement: <span class="warn-content">{{ contentScores.highScore }}</span></p>
            <p class="dropdown-item-text">Votre meilleur score est: <span class="warn-content">{{ contentScores.userScore.score }}</span></p>

I don't understand why i can't instantiate an object undefined with a value in the api call. Notice that the console.log show the value so isn't a problem from the recovered data the issue is about the declaration of my object storing the values.

I'm pretty new to the Angular7 development so sorry if the answer might be obvious and sorry in advance for my english, i know it isn't really good.

Upvotes: 0

Views: 40

Answers (1)

rpadovani
rpadovani

Reputation: 7360

contentScores has not been initialized.

If you init it with an empty object then it will work

contentScores: {
    highScore: number,
    userScore: Score
  } = {};

This is due how the code is transcompiled to Javascript. Take a look to this Typescript code:

class Greeter {
    contentScores: {
        highScore: string,
    };
    constructor(message: string) {
        this.contentScores.highScore = message;
    }
}

let greeter = new Greeter("world");

It will become this Javascript code:

var Greeter = /** @class */ (function () {
    function Greeter(message) {
        this.contentScores.highScore = message;
    }
    return Greeter;
}());
var greeter = new Greeter("world");

As you can see, this.contentScores isn't initialized anywhere.

On the other hand, if you create it as an empty object, then it will be properly initialized:

class Greeter {
    contentScores: {
        highScore: string,
    } = {};
    constructor(message: string) {
        this.contentScores.highScore = message;
    }
}

let greeter = new Greeter("world");

becomes

var Greeter = /** @class */ (function () {
    function Greeter(message) {
        this.contentScores = {};
        this.contentScores.highScore = message;
    }
    return Greeter;
}());
var greeter = new Greeter("world");

Upvotes: 1

Related Questions