Ionic 2 - this keyword is undefined when promise is returned from data service


#1

I have a method in my data service which selects records from SqlStorage and returns the promise object…

import {Storage, SqlStorage} from 'ionic-angular';
import {Injectable} from 'angular2/core';
import {Journey} from '../../models/journey-model';
@Injectable()
export class Data {
    db: any;
    constructor() {
        this.db = new Storage(SqlStorage, { name: 'journeyData.js' });
    }
    getJourneys() {
        let queryString = 'SELECT * FROM journey ORDER BY endTime DESC';
        return this.db.query(queryString); 
    }
}

And I have a page which uses the data service…

import {Page, NavController} from 'ionic-angular';
import {Data} from '../../providers/data/data';
@Page({
    templateUrl: 'build/pages/journey-history/journey-history.html',
})
export class JourneyHistoryPage {
    isLoading: boolean;
    journeyList = [];
    constructor(public nav: NavController, private dataService: Data) {
        this.nav = nav;
        this.dataService = dataService;
    }
    getJourneys() {
        this.isLoading = true;
        this.dataService.getJourneys().then((response) => {
            if (response.res.rows.length > 0) {
                for (let journey of response.res.rows)  this.journeyList.push(journey);
            }
            this.isLoading = false;
        }, (err) => {
            this.isLoading = false;
        });
    }
    refreshList(refresher) {
        setTimeout(() => { refresher.complete(); }, 1000);
    }
    onPageWillEnter() {
        this.getJourneys();
    }
}

And this is the page view

<ion-navbar *navbar teale>
    <ion-title>Journey App</ion-title>
</ion-navbar>
<ion-content padding class="app-background">
    <div *ngIf="isLoading" class="text-center">       
        <ion-spinner name="circles"></ion-spinner>
    </div>
    <div *ngIf="!isLoading">
        Display list...
    </div>
</ion-content>

Steps that happen are:

  1. On page enter getJourneys() is called.

  2. In here the isLoading variable is set to true and the ion-spinner is displayed.

  3. Data is then fetched from the data service and returned as a promise.

  4. Returned objects are pushed into a list and the isLoading variable is set to false.

my problem is that when I loop through the data in the promise the this keyword is undefined but it should still be the JourneyHistoryPage am I right? so the variables on the page won’t update and the ion-spinner continues to display.

Can anybody tell me if I have done something wrong here?


Database provider and it's promises
#2

IMHO your code looks OK.

Are you sure that this is really undefined, or maybe there’s another problem which prevents the data from updating? Does getJourneys() return any data? What’s your project setup, e.g. the output of ionic info?

I would suggest you to modify your code as follows and observe the output in the dev-tools console:

            console.log('this is of type: ' + typeof(this));
            console.log(response.res.rows.length + ' rows returned...');
            if (response.res.rows.length > 0) {
                for (let journey of response.res.rows) {
                  console.log(journey);
                  this.journeyList.push(journey);
                }
            }

#3

This is my project information:

Cordova CLI: 5.4.0
Gulp version: CLI version 3.8.11
Gulp local: Local version 3.9.1
Ionic Framework Version: 2.0.0-beta.4
Ionic CLI Version: 2.0.0-beta.24
Ionic App Lib Version: 2.0.0-beta.14
OS: Windows 8.1
Node Version: v4.4.2

And I’m certain it returns data because I follow it through the loop using a debugger; and its at this point I hover over the this keyword and it says undefined.

I will provide more information tomorrow im just about to finish work now. I will include screen shots and html code too.


#4

Like I said here is some more info. I’ve added my page view code to the question and here is a screenshot of chrome debugger and the scope variables…

For some reason this is undefined but if I set it to a new variable e.g. let test = this; then test contains the right values but the page view still doesn’t update in the *ngIf="" parts.

Update

I missed the part where you told be to console.log(typeof(this)); and it prints out object so maybe it is not undefined and chrome dev tools are just lying to me haha. I still don’t understand why the variables don’t update on the page view though.

Also console.log(this.isLoading); prints out false so the variables are updating but not being recognised by the ngIf i have in the view.


#5

I guess that maybe the arrow functions are still not supported in Chrome Dev Tools and that’s why this inside of them is reported as undefined. It seems that the updates to the properties are not being detected by Angular. This might be related to the following issue (you can try the workaround in this topic, it should fix the problem):


#6

@iignatov you life saver! I don’t think I would have spotted that in a million years. Something so important and seems to be missing from a lot of old tutorials and documentation.

This tutorial was recently updated and explains the use of ngZone.

Anybody reading this and has the same problem of views not updating here is how my page now looks…

import {NgZone} from 'angular2/core';
import {Page, NavController} from 'ionic-angular';
import {Data} from '../../providers/data/data';

@Page({
    templateUrl: 'build/pages/journey-history/journey-history.html',
})
export class JourneyHistoryPage {
    isLoading: boolean;
    journeyList = [];
    constructor(public nav: NavController, private dataService: Data, private zone: NgZone) {
        this.nav = nav;
        this.isLoading = false;
    }

    getJourneys() {
        this.isLoading = true;
        this.dataService.getAllJourneys().then((response) => {
            this.zone.run(() => {
                if (response.res.rows.length > 0) {
                    for (let journey of response.res.rows) this.journeyList.push(journey);
                }
                this.isLoading = false;
            });
        }, (err) => {
            this.zone.run(() => {
                this.isLoading = false;
            });
            console.log('Fetch journeys error: ' + JSON.stringify(err));
        });
    }

    onPageWillEnter() {
        this.getJourneys();
    }
}

#7

A short update: this was caused by a bug fixed in beta.6 (check out the linked post for details):


#8

Thanks for the update :smile: I will update my project now!!!