Problems with JSON processing ("undefined is not an object")


#1

Hey Ionic community,
I just discovered Ionic and I’m trying to build a very simple app that gets json data from a server, then displays it as cards with a tap on one of the cards opening a detail view of the item.
My problem is that I get the error EXCEPTION: TypeError: undefined is not an object (evaluating 'l_gameDetails1.gameName') in [{{ gameDetails.gameName }} in DetailsPage@8:25] and that my page displays no content.
The involved files look like this:

details.ts:

import {Page,NavController, NavParams} from 'ionic-angular';
import {OnInit} from 'angular2/core';
import {GameService} from "../../../services/game-service";

@Page({
    templateUrl: 'build/pages/details/details.html',
    providers: [GameService]
})

export class DetailsPage implements OnInit{
    public game;
    public gameDetails;
    constructor (private _github: GameService, private _nav: NavController, private _navParams: NavParams){
        this.game = _navParams.get('game');
    }
    ngOnInit(){
        this._github.getDetails(this.game).subscribe(
            data => this.gameDetails = data.json(),
            err => console.error(err),
            () => console.log('getDetails completed')
        );
    }
}

details.html:

<ion-navbar *navbar>
    <ion-title>
        {{ game.gameName }}
    </ion-title>
</ion-navbar>

<ion-content>
    <ion-card>
        <ion-card-header>
            {{ gameDetails.gameName }}
        </ion-card-header>
    </ion-card>
</ion-content>

game-service.ts:

import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';

@Injectable()
export class GameService {
    constructor(private http: Http) {
    }

    getGames() {
        let games = this.http.get(`http://some.api.url`);
        return games;
    }
    getDetails(game){
        let detail = this.http.get(`http://some.api.url`);
        return detail;
    }
}

The API’s json response looks like this:

{"gameName":"Stardew Valley"}

I tried to debug the error and found out that when I set the value of gameDetails in details.ts manually (e.g. public gameDetails = {gameName: "test"};) and leave the ngOnInit method blank everything seems to work fine.

My best (not very educated) guess is that there is some kind of timing problem due to the JSON not loading as fast as the page. :innocent:

What can I do to fix my problem?

Thanks in advance for the help. :slight_smile:


#2

Change your code this way and it should work:

{{ gameDetails?.gameName }}

The ?. (Elvis operator) means that the template field is optional and if undefined, the rest of the expression should be ignored.

I’ll recommend you to check out the Angular 2 Cheatsheet for a quick start.


#3

Another way to handle this is to always ensure that gameDetails is defined, by setting a dummy up in the constructor:

this.gameDetails = {gameName: this.game.gameName};

You also have a lot of implicit any typed variables and subroutine return types. If you set "noImplicitAny": true in your tsconfig.json, tsc will yell at you about these and help you type things properly. That’s much of the purpose of using TypeScript in the first place.


#4

Thanks for the help, your solution works great!


#5

Also thanks for your help and the implicit any hint. I’ll now try to refactor my code to use less and hopefully no any types one day.
Another small question: In order to avoid implicit any I set up some classes (e.g. a “Game” class in my project. My intuition is to set up setter and getter methods for all attributes of the classes and keep them private but I read somewhere that in Typescript you’re supposed to just access your classes attributes directly. What is the best practice here?

Thanks in advance for the help :slight_smile:


#6

I’m hardly in a position to comment on best practices, but my opinion for what it’s worth:

I’m not a big fan of the dogma of “thou shalt make getter/setter functions for everything, lest the Deity of Encapsulation smite thee down”. If all the methods do is get and set, I don’t see the point. If setting a field to a different value, requires invalidating some internal cache, then it makes sense to have that centralized in a setter function, instead of relying on all client code calling invalidateCache().

For something like your Game class, you probably will be satisfied with an interface instead of a class. About the only time I’ve found TypeScript classes needed instead of interfaces is when object lifecycle is complex. When the primary use case is “get JSON from someplace and reanimate it”, interfaces do just fine, and you still get all the type-checking benefit of not misspelling property names (and attendant autocompletion).


#7

Thanks a lot, I’ll go with an interface then. :slight_smile:


#8

Hello if you face problem on this game you can try on games like stardew valley this is best qulity user friendly game Thanks