How to implement promises inside Ionic/Cordova

Viewed 939

I am building an Ionic app that works using token based authentication. When the user auths an access token is stored in local storage.

My code then calls a provider for whatever data it needs (in this case it's pulling 'Training Programmes' from an API).

In turn, that provider calls another provider which handles HTTP requests for my API. It effectively sets the required headers and initiates the HTTP request, then returns the response back up to the requesting provider.

This all works fine, until I need to get the access token from storage before making the HTTP request. It seems that accessing data from storage is not instant, and instead uses the then syntax.

It seems like when I call the provider from my code it passes down to the Training Programmes provider, and then down to the API provider. The code here then runs and passes back up, but this happens before my app has chance to grab what I need from storage.

I tried placing everything in the request inside the then call of the storage.get method, but that didn't work.

Here is my requesting code:

this.trainingProgramme.get_programmes().subscribe((res) => {
  loader.dismiss();
  console.log(res);
});

Which filters down to my trainingProgramme provider:

  get_programmes() {
    let seq = this.api.get('training-programmes');

    seq
      .map(res => res.json())
      .subscribe();

    return seq;
  }

Which in turn passes down to my api provider:

export class Api {
  base_url: string = 'https://******.com';
  url: string = this.base_url + '/api/v1';      
  access_token: string;

  constructor(
    public http: Http, 
    private storage: Storage) {

    // Grab access token and store it
    storage.get('access_token').then((val) => {
      console.log('Pulled access token from local storage', val);
      this.access_token = val;
    });
  }

  // Performs a GET request with auth headers
  get(endpoint: string, params?: any) {
    let headers = new Headers();
    headers.append('Authorization', 'Bearer ' + this.access_token);

    let options = new RequestOptions({ headers: headers });

    return this.http.get(this.url + '/' + endpoint, options);
  }
}

This does not produce any errors but the headers being sent through are:

Authorization: Bearer undefined

In summary: I believe the storage call is happening asynchronously, so the HTTP call is initiated before it has chance to grab the access token. How can I prevent this from happening or wait for storage to finish before calling the HTTP request? Simply placing my code inside then won't work, as this method is already sat within subscribes, so I get errors.

UPDATE 1:

Below is the code I have used placing the storage code inside the method itself, rather than in the constructor, and then placing all of the code, including the HTTP call, inside the then.

  // Performs a GET request with auth headers
  get(endpoint: string, params?: any) {
    // Grab access token and store it
    this.storage.get('access_token').then((val) => {
      console.log('Pulled access token from local storage', val);
      this.access_token = val;

      let headers = new Headers();
      headers.append('Authorization', 'Bearer ' + this.access_token);

      let options = new RequestOptions({ headers: headers });    

      return this.http.get(this.url + '/' + endpoint, options);
    });
  }

The errors with:

Runtime Error
Uncaught (in promise): 
TypeError: Cannot read property 'map' of undefined 
TypeError: Cannot read property 'map' of undefined at TrainingProgramme.get_programmes 

Presumably this is because the get method doesn't return anything when expected, as the return is now asynchronous.

2 Answers
Related