Global provider not initialised when reaching rootpage

Hi All, I have this issue with my Ionic 2 app which seems to be a timing / init issue.

I have a app-wide dataservice (GlobalService) which is added to the providers array in app.module. In all pages that require data, I import the GlobalService and inject it in the constructor of the component. see snippets below.

Now this all works as expected as long as I assign the rootpage in app.component to a page that does not inject the Global Service.

When I set the rootpage to a page that imports and injects the GlobalService. All method calls of to the Global service result in “Cannot read property ‘xyz’ of undefined” errors.

It seems that GlobalService is not initialized yet when the rootpage is assigned to a page that is using GlobalService.

Hope I am making myself clear…

Any ideas how to approach this?

app.module
import { NgModule, ErrorHandler } from ‘@angular/core’;
import { IonicApp, IonicModule, IonicErrorHandler } from ‘ionic-angular’;
import { MyApp } from ‘./app.component’;

import { Dashboard } from '../pages/dashboard/dashboard';
import { Settings } from '../pages/settings/settings';
import { Notifications } from '../pages/settings/notifications/notifications';
import { About } from '../pages/settings/about/about';
import { Version } from '../pages/settings/version/version';
import { AddMemberToGroupPage } from '../pages/team/add-member-to-group';
/*import { Storage } from '@ionic/storage';*/

@NgModule({
  declarations: [
    MyApp,
    Dashboard,
    Settings,
    Notifications,
...
...
    AddGroupPage,
    AddMemberToGroupPage
  ],
  imports: [
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    Dashboard,
    Settings,
    Notifications,
    ...
    ...
    AddGroupPage,
    AddMemberToGroupPage
  ],
  providers: [GlobalService, {provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}

Dashboard page using the globalService

import { Component } from '@angular/core';
//import { NavController } from 'ionic-angular';
import { GlobalService } from '../../providers/global-service';

@Component({
  selector: 'page-dashboard',
  templateUrl: 'dashboard.html'
})
export class Dashboard {

  userprofile: any;

  constructor(public ds: GlobalService) {
      console.log("invoked Dashboard.Constructor");
  }

  getUserProfile() {
    this.ds.getUserProfile(this.userid)
    .then( data => {
    this.userprofile = data;
    console.log('userprofile=%s', JSON.stringify(data));
    })
    .catch(error => alert(error));
  }
}

I’m confused because it seems that all you’ve talked about is when things work, and haven’t described what the problem is.

Ah, good point, thanks, I updated the question

Probably going to have to see more code here.

I’ve included app.module and dashboard page using GlobalService

Well, if the problem is that you have things in your template like {{userprofile.xyz}}, they are going to puke because userprofile isn’t populated until getUserProfile() resolves. What I would do is define a business logic interface and initialize userprofile with a dummy:

interface UserProfile {
  abc: string;
  xyz: string;
};

export class Dashboard {
  userprofile: UserProfile = {abc: 'loading', xyz: 'loading'};
}

I’ve made the changes but did not make a difference. The error messages only occur when the callouts to GlobalService are made. For this test I explicitly call the getUserId method with a button on the page template.

GlobalService seems not to be initialized yet. I am getting ‘getUserId’ is undefined so for some reason the Dashboard page injects the GlobalService provider before it is fully initialized.

error_handler.js:49 ORIGINAL EXCEPTION: Cannot read property 'getUserId' of undefined
ErrorHandler.handleError @ error_handler.js:49
IonicErrorHandler.handleError @ ionic-error-handler.js:56
next @ application_ref.js:272
schedulerFn @ async.js:82
SafeSubscriber.__tryOrUnsub @ Subscriber.js:223
SafeSubscriber.next @ Subscriber.js:172
Subscriber._next @ Subscriber.js:125
Subscriber.next @ Subscriber.js:89
Subject.next @ Subject.js:55
EventEmitter.emit @ async.js:74
NgZone.triggerError @ ng_zone.js:278
onHandleError @ ng_zone.js:257
t.handleError @ polyfills.js:3
e.runTask @ polyfills.js:3
invoke @ polyfills.js:3
error_handler.js:52 ORIGINAL STACKTRACE:
ErrorHandler.handleError @ error_handler.js:52
IonicErrorHandler.handleError @ ionic-error-handler.js:56
next @ application_ref.js:272
schedulerFn @ async.js:82
SafeSubscriber.__tryOrUnsub @ Subscriber.js:223
SafeSubscriber.next @ Subscriber.js:172
Subscriber._next @ Subscriber.js:125
Subscriber.next @ Subscriber.js:89
Subject.next @ Subject.js:55
EventEmitter.emit @ async.js:74
NgZone.triggerError @ ng_zone.js:278
onHandleError @ ng_zone.js:257
t.handleError @ polyfills.js:3
e.runTask @ polyfills.js:3
invoke @ polyfills.js:3
error_handler.js:53 TypeError: Cannot read property 'getUserId' of undefined
    at GlobalService.getUserId (global-service.ts:19)
    at Dashboard.getUserId (dashboard.ts:35)
    at CompiledTemplate.proxyViewClass.View_Dashboard0.handleEvent_16 (/AppModule/Dashboard/component.ngfactory.js:370)
    at CompiledTemplate.proxyViewClass.<anonymous> (view.js:408)
    at HTMLButtonElement.<anonymous> (dom_renderer.js:276)
    at t.invokeTask (polyfills.js:3)
    at Object.onInvokeTask (ng_zone.js:227)
    at t.invokeTask (polyfills.js:3)
    at e.runTask (polyfills.js:3)
    at HTMLButtonElement.invoke (polyfills.js:3)
ErrorHandler.handleError @ error_handler.js:53
IonicErrorHandler.handleError @ ionic-error-handler.js:56
next @ application_ref.js:272
schedulerFn @ async.js:82
SafeSubscriber.__tryOrUnsub @ Subscriber.js:223
SafeSubscriber.next @ Subscriber.js:172
Subscriber._next @ Subscriber.js:125
Subscriber.next @ Subscriber.js:89
Subject.next @ Subject.js:55
EventEmitter.emit @ async.js:74
NgZone.triggerError @ ng_zone.js:278
onHandleError @ ng_zone.js:257
t.handleError @ polyfills.js:3
e.runTask @ polyfills.js:3
invoke @ polyfills.js:3
error_handler.js:56 ERROR CONTEXT:

Can you show us GlobalService?

Here you go…

import {Injectable} from '@angular/core';
import {DataService} from 'forcejs';

@Injectable()
export class GlobalService {

    service: any;
    userid: string;
    schoolid: string;

    constructor() {
        console.log('UserProfileService.Constructor invoked.');
        this.service = DataService.getInstance();
        console.log('this.service=%s', this.service);


    }

    getUserId() {

        this.userid = this.service.getUserId();
        return this.userid;
    }

    getSchoolId() {

        this.schoolid = 'a345E0000004vSFQAY';
        return this.schoolid;
    }

    getUserProfile (userId) {

      var pathparam = {
            path: "/user/" + this.service.getUserId(),
            params: {tpe: "basic_profile"} };

      console.log('pathparam%s', pathparam);

      return this.service.apexrest(pathparam);
    }

    getUserSettings (userId) {

      var pathparam = {
            path: "/user/" + this.service.getUserId(),
            params: {tpe: "app_settings"} };

      console.log('pathparam%s', pathparam);

      return this.service.apexrest(pathparam);
    }

    createTeamMember (first, middle, last, email,  ) {

      var requestdata = {"basic_profile": {
		                     "first_name": first,
		                     "middle_name": middle,
		                     "last_name": last,
		                     "email": email
	                      },
	                      "school_relation_role": "Teacher"
      };

      var pathparam = {
                  path: "/school/" + this.getSchoolId(),
                  params: {tpe: "add_teammember"},
                  data: requestdata
      };

      console.log('pathparam=%s', JSON.stringify(pathparam));

      return this.apexrestwithbody(pathparam);
    }

// temp quickfix, function should move to force.js
    apexrestwithbody(pathOrParams) {

        let obj;

        if (typeof pathOrParams === "string") {
            obj = {path: pathOrParams, method: "GET"};
        } else {
            obj = pathOrParams;

            if (obj.path.charAt(0) !== "/") {
                obj.path = "/" + obj.path;
            }

            if (obj.path.substr(0, 18) !== "/services/apexrest") {
                obj.path = "/services/apexrest" + obj.path;
            }
        }

        if (!obj.contentType) {
            obj.contentType = (obj.method == "DELETE" || obj.method == "GET" ? null : "application/json");
        }

        obj.method = "POST";

        console.log('obj=%s', JSON.stringify(obj));

        return this.service.request(obj);
    }

}

Does this fire with a reasonable value? Can you tell if it fires before or after GlobalService.getUserId is called? It looks to me like GlobalService is being injected fine, but there seems to be some problem inside it with the Salesforce service.

nope, this.service=undefined

Now the strange thing is, if I assign rootpage (in app.component) to a page that does not inject Global Service. The GlobalServices works fine for the other pages that do inject Global Service.

I don’t know how you are doing your OAuth authentication (if you are), so I don’t know what other things are needing to happen before DataService.getInstance() is going to work. Whatever they are, including the DataService.getInstance() call itself, may need to be guarded in platform.ready().then(() => {});.

I’m doing the Oauth stuff in the constructor of MyApp (app.component), see below.

Ah so, the getInstance may be happening before Oauth.

I will give it a try. Thanks a lot so far.

Mark

let oauth = OAuth.createInstance('<my appid>', '<my url>', 'http://localhost:8200/oauthcallback.html');
oauth.login()
    .then((oauthData) => {....