Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement promises inside Ionic/Cordova

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.

like image 519
Mike Avatar asked Jul 13 '17 08:07

Mike


2 Answers

try this :)

yourlongmethod() {

  return new Promise((resolve, reject) => {

     //your code
     var data = "Any data you want to return with the promise";

    resolve(data);
  });

}

Then you can call this

yourlongmethod().then((res) => {

console.log(res) })

like image 64
Diluk Angelo Avatar answered Nov 08 '22 07:11

Diluk Angelo


In side your api provider's constructor you are writing your code for getting the stored data which is asynchronous remember JavaScript is synchronous so once the asynchronous is been called i mean store.get().then() it is completed the constructor part it is not waiting to get stored the value to the this.access_token and it is calling the get() function.

In order to tell the storage.get async call to wait until it is completed and then go and call the get() method just use the key word async and await

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

   this.getAccessToken(); //only after completing this function it will go the next command
  }

  async getAccessToken(){
      // Grab access token and store it
      //below i have writen await inorder to wait until this is completed.
    const token = await storage.get('access_token').then((val) => {
      console.log('Pulled access token from local storage', val);
      this.access_token = val;
    });
  }

try to place the above code and check hope it helps you

like image 28
Mohan Gopi Avatar answered Nov 08 '22 06:11

Mohan Gopi