Accessing global provider variable from different pages = ERROR

Hiya! Newbie here, I was looking around for a problem this specific but I havent found anything… so here goes!

My ionic app is structured this way:

MainPage
SectorPage

When you select a Sector on the MainPage, it will show you SectorPage but with specific information. So if I click on “Water Sector” in MainPage it should change some things of SectorPage to visually act accordingly: a blue background color, for example.

So these colors are stored in the globals provider, which is initialized when the IntroPage is loaded at first, like this:

export class IntroPage {
    constructor(public platform : Platform, ...){
       platform.ready().then(() => {
  		this.globals.setVariables();
  	});
    }
}

Then I clic WaterSector and it would go as follows:
HTML: <button (click)="openSector(0)">WaterSector</button>
TypeScript:

openSector(id) {
  	this.navCtrl.push('SectorPage', {id:id});
  }

Then SectionPage has NavParams on its constructor, and the id number passes just fine:

export class SectionPage {
  constructor(public navCtrl: NavController, public navParams: NavParams, 
  public globals : GlobalsProvider) {
  	console.log(this.navParams.data); [returns 0 - OK]
  }
}

Then on GlobalsProvider (EDIT: rest of the code on reply below, to avoid filling too much on this post)…

getColor (id : number){
     console.log(id);
     return this.sectionColors[id].color;  
}

At this step, I get an error that says:

Error: Uncaught (in promise): TypeError: Cannot read property ‘color’ of undefined
TypeError: Cannot read property ‘color’ of undefined

sectionColors does exist! It’s an array where each spot has an id and a color but seemingly it lost its data in the transition between one page to another, even though all this happens in a provider!

What am I doing wrong here? :frowning_face:
Any tips will be deeply appreciated!

Thanks in advance!

1 Like
  1. Please properly format your code so we can read it.
  2. Show us the rest of GlobalsProvider, because we don’t know what sectionColors is or where anything else could be.
  3. Show us where you set sectionColors in the first place

Noted! Hope the changes I made to the post are ok.

Here’s the rest of Globals Provider:

@Injectable()
export class GlobalsProvider {

	public sectionColors : any;

  constructor(public http: Http) {
    
  }

  setVariables (){
  	this.http.get('assets/data/sectionColors.xml')
      .map(res => res.text())
      .subscribe((data)=>
      {
         this.parseXML(data)
         .then((data)=>
         {
            this.sectionColors = data;
            console.log(this.sectionColors);
         });
      });
      
  }

  parseXML(data){
      return new Promise(resolve =>
      {
         var k,
             arr    = [],
             parser = new xml2js.Parser(
             {
                trim: true,
                explicitArray: true
             });

         parser.parseString(data, function (err, result)
         {
            var obj = result.sectionColors;
            for(k in obj.section)
            {
               var item = obj.section[k];
               arr.push({
                  id		: item.id[0],
                  number	: k,
                  color		: item.color[0]
               });
            }

            resolve(arr);
         });
      });
   }

   getColor (id : number){
     console.log(id);
   	    return this.sectionColors[id].color;
      
   }

}
1 Like

Looks way better, thanks! Okay, a couple thoughts for you.

First on a minor note, you shouldn’t need to wait for platform.ready to set those variables, you’re not using any phone hardware there. I would however recommend you move all logic out of the constructors and putting them in ngOnInit, and having your pages implement OnInit (google that for Angular if you aren’t familiar).

Then, I don’t see where you call getColors. I see it’s definition, but in your SectionPage I only see you logging the value. You call it too right?

In your setVariables method, you’re nesting a promise inside your observable subscribe. This may not be your issue, but it could definitely cause you some issues. I would recommend either converting your http.get to a promise, you converting your parseXML to an observable so you can chain those instead of nest them. With nesting you can get all sorts of issues, like errors being swallowed. With async/await I generally prefer promises unless I need some of the magic of observables, which in this case I don’t think you do.

Consider this example:

async setVariables() {
  try {
    const results = await this.http.get('assets/data/sectionColors.xml').toPromise();
    this.sectionColors = await this.parseXML(results.text());
    console.log(this.sectionColors); // make sure this logs the colors
  } catch (error) {
    console.log(error);
  }
}

As I put in the comment in the code sample, make sure that the console.log does in fact log out your colors. Perhaps post on here what it logs out if you still have issues.

If that logs out properly, then that variable should be set. The next question is, where are you providing that provider from? The base app module?

Wow! What an elaborate reply! I was not expecting that much detail, thank you!!

Now to answer some things:

Yeah (facepalm) I do call the method later on the same function like this:

globals.getColor(navParams.data)

Yet what I get from GlobalsProvider is undefined.

Yes, I should definitely try that, adding that I’m not entirely sure how nesting, and promises, observables etc work, as I’m only following the Ionic side of the framework :grimacing:
I’m gonna have to brush up my Angular knowledge,thats for sure.

Yes, it is provided from the base app module, as it was directly set when I created a provider through Terminal.

Let me tinker with your magic code and get back to you, thank you again!!

This seems quite sketchy to me, shouldn’t this be globals.getColor(navParams.get('id'))?

Holy crap THAT’S what it is!! :scream:

Oh my god, so simple… xD

Anyway, I won’t leave your previous comment unaccounted for, I should definitely try my luck with async methods and OnInit initializations.

Thanks a million!!