Injectables and global variables


#1

I’m having some difficulties with Ionic 2 and setting up global variables. The structure of my app is as follows:

Main app
  |
  |--- Page1 (Info)
  |--- Page2 (Map)
  |--- Page3 (List)
         |
         |--- ItemTabsPage
                |
                |---tab1
                |---tab2
                |---tab3

My intention is to show a list in Page3, and once one item is selected, to show additional information in tabs.

I send the information from Page 3 to the page with the tabs using:

itemTapped(event, item) {
    this.nav.push(ItemTabsPage, {
      item: item
    });
  }

The problem is that I can’t do the same to send the info to the child tabs. I would like to show different information depending on which item is selected. I have tried defining an injectable globalVars.js to store the value in a variable:

import {Injectable} from 'angular2/core';

@Injectable()
export class GlobalVars {

  constructor(myGlobalVar) {
    this.myGlobalVar = "";
  }

  setMyGlobalVar(value) {
    this.myGlobalVar = value;
  }

  getMyGlobalVar() {
    return this.myGlobalVar;
  }

}

and then updating the code of itemTapped in the list as follows:

itemTapped(event, item) {
        this.nav.push(ItemTabsPage, {
          item: item
        });
        this.globalVars.setMyGlobalVar(item);
      }

However, I always get the same error:

Uncaught EXCEPTION: Error during evaluation of "click"
ORIGINAL EXCEPTION: TypeError: Cannot read property 'setMyGlobalVar' of undefined

The code for page3 is:

import {Page, NavController, NavParams} from 'ionic-angular';
import {ItemService} from '../services/ItemService';
import {ItemTabsPage} from '../item/item-tabs/item-tabs';
import {GlobalVars, setMyGlobalVar} from '../../providers/globalVars';

import {Http} from 'angular2/http';
import 'rxjs/add/operator/map';

@Page({
  templateUrl: 'build/pages/item-list/item-list.html',
  providers: [ItemService]
})

export class ItemListPage {

  static get parameters() {
    return [[NavController], [NavParams], [Http]];
  }

  constructor(nav, navParams, http, globalVars) {
    this.nav = nav;
    // If we navigated to this page, we will have an item available as a nav param
    this.selectedItem = navParams.get('item');
  	this.http = http;
  	//this.items = null;
    this.globalVars = globalVars;


  	this.http.get('https://website-serving-the-info.com/items.json').map(res => res.json()).subscribe(data => {
  		this.items = data.items;
        },
      err => {
          console.log("Oops!");
  	});

  }

  itemTapped(event, item) {
    this.nav.push(ItemTabsPage, {
      item: item
    });
    this.globalVars.setMyGlobalVar(item);
  }
}

Anyone have any suggestion? My Ionic installation is:

Cordova CLI: 6.1.1
Gulp version:  CLI version 3.9.1
Gulp local:   Local version 3.9.1
Ionic Framework Version: 2.0.0-beta.4
Ionic CLI Version: 2.0.0-beta.25
Ionic App Lib Version: 2.0.0-beta.15
OS: Distributor ID:	LinuxMint Description:	Linux Mint 17.3 Rosa 
Node Version: v5.11.0

I tried adding [GlobalVars] to the return in shop-list.js but if I do that, the app gets blank and it doesn’t start…


#2

I don’t use es6, but I think, you need to add your class to parameters.


#3

In addition to what xr0master said, I don’t think GlobalVars is injectable as is, because of the parameter to its constructor. I would get rid of that, especially because it’s not even used.


#4

Thank for your answers.

How to do suggest me to do it then? I have been looking for good explanations of injectables without luck.

And yes, I’m used to use ES6 and I’m still make many mistakes…


#5

Did you add the Injectable as a provider in your app.ts?


#6

Thanks for the answer.

Yes, I added the injectable to the app.js.

I uploaded the code to https://github.com/realjumy/menuPlusTabs because something is failing and I don’t know what it is. For sure it’s a concept I have wrong, but I can’t identify what.


#7

Ok, I have almost solved this, but there’s something that is not working properly.

This is my injectable globalVars.js:

import {Injectable} from 'angular2/core';
import {Storage, LocalStorage} from 'ionic-angular';

@Injectable()
export class GlobalVars {
  static get parameters(){
  }


  constructor() {
    this.storage = new Storage(LocalStorage);
    

  }

  setMyGlobalVar(value) {
    this.storage.set("MY_GLOBAL_VAR", value);
    console.log("The value received to store is: " + value);
  }

  getMyGlobalVar() {
    return this.storage.get("MY_GLOBAL_VAR").then((value) => {
      console.log(">>>: "+value);
      return value;
    });
  }

}

And this is the code inside of page1.js:

import {Page, NavController, NavParams} from 'ionic-angular';
import {ShopsTabsPage} from '../shop-tabs/shop-tabs';
import {GlobalVars, getMyGlobalVar} from '../../../providers/globalVars';


@Page({
  templateUrl: 'build/pages/shop/page1/page1.html',
  providers: [[GlobalVars]]
})
export class ShopInfoTab {
  static get parameters() {
    return [[NavController], [NavParams], [GlobalVars]];
  }
  constructor(nav, navParams, globalVars) {
    
    this.nav = nav;
    
    this.globalVars = globalVars;
    this.selectedItem = this.globalVars.getMyGlobalVar();
    console.log("Item: " + this.selectedItem);
    console.log("+++++++" + this.globalVars.getMyGlobalVar());
  }
}

And it’s pretty strange, because I can store the value of the variable, and inside of the injector I can see it properly, but when I use return and I try to use it in the destiny, it appears as [object Object].

This is the output of the console:
The value received to store is: Shop 1
Storing the value: Shop 1
The shop is: Shop 1
Item: [object Object]
+++++++ [object Object]
>>>: Shop 1

As you can see, when I retrieve the data inside of the injectable it appears properly (the value after >>>), but if I use return to transfer it, what I receive is just the [object Object].

Any suggestion of what can I do?

Thanks.


#8

My first suggestion is “stop using global variables for this”. There is documentation about passing data to tabs here.

That being said, that [object Object] is the promise you are returning from getMyGlobalVar(). You are not returning the value itself. “How do I return the value itself?” is generally the next question. You can’t. Asynchronous programming doesn’t work that way. The value is not ready when you call get. You can say what you want to do once the value is available (as you do with then), but you can’t (well, more accurately “shouldn’t”) block the entire VM to wait for it.


#9

Thank you very much for your answer @rapropos.

I was taking a look to that documentation before writing the question here last week, but I wasn’t able to make it work that way. That’s why I decided trying with injectors. I will try again to understand how passing data works tomorrow, maybe I’m lucky.

Cheers, man.


#10

@rapropos I need some help to understand this.

In the documentation the following example is given:

export class Tabs {
  chatRoot = ChatPage;

  // set some user information on chatParams
  chatParams = {
    user1: "admin",
    user2: "ionic"
  };

  constructor() {

  }
}

However, I don’t know how to translate the section before the constructor into ES6. I also tried using directives but any success yet. I have the feeling I making really complicated something that is really simple…


#11

You’re accessing local storage which is an asynchronous operation, meaning that the data you are trying to get is not available instantly, which is why a promise is used. You’re using the promise correctly in your injectable, but the way you’re trying to access it doesn’t make sense.

The function in your constructor should look more like this:

  getMyGlobalVar() {
    return this.storage.get("MY_GLOBAL_VAR");
  }

You don’t want to handle the promise in here, you just want to return it. Then when trying to access it from page1.js you should do this:

this.globalVars.getMyGlobalVar().then((value) => {
    this.selectedItem = value;
});

#12

Thank you very much, it works. now :slight_smile: