4

I am currently building an application with Ionic 2 and using the Storage plugin to hold my values which are pretty much just an API Token and user profile since the application pulls all data from an API.

I am testing the application via ionic serve because no native functions are used but now I am facing the problem that every time I store a value in the Storage the value is not accessible until I reload the app which is kind of annoying because after the user logs in he gets redirected to a page that requires the API token which is not available until I reload the app so the whole thing gets stuck in a loop.

Ionic Storage is using IndexedDB in the browser where I can see that the values have been stored when I check them with Chrome Developer tools.

I have been trying to figure out the issue but can't find any reason why the storage values are not available until reloading the app.

import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';
import { HttpClientService } from './http-client-service';
import 'rxjs/add/operator/map';

@Injectable()
export class AuthService {
    constructor(public events: Events, public storage: Storage, public http: HttpClientService) {
        //
    }

    login(user) {
        var response = this.http.post('login', {
            email: user.email,
            password: user.password,
        });

        response.subscribe(data => {
            this.storage.set('api_token', data.token);

            console.log('raw : ' + data.token); // shows the api token

            this.storage.get('api_token').then((value) => {
                console.log('storage : '+ value); // is empty...
            });
        });

        return response;
    };
}

Edit: I managed to track down the issue to the storage running async which results in the token not being added to the headers.

createAuthorizationHeader(headers: Headers) {
    // this does add the header in time
    localStorage.setItem('api_token', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vYXBpLndpaHplLmRldi9sb2dpbiIsImlhdCI6MTQ4MTE4MzQyOCwiZXhwIjoxNDgxMTg3MDI4LCJuYmYiOjE0ODExODM0MjgsImp0aSI6IjdlNTE1WUEwWmE4NWc2QjUiLCJzdWIiOiIxIiwidXNlciI6eyJpZCI6MX19.T4KpqgCB8xU79vKyeLG4CJ0OHLpVI0j37JKIBJ_0CC4');
    headers.append('Authorization', 'Bearer ' + localStorage.getItem('api_token'));

    // this does not add the header in time
    return this.storage.get('api_token').then((value) => {
        headers.append('Authorization', 'Bearer ' + value);
    });
}

getHeaders(path) {
    let headers = new Headers();
    headers.set('Accept', 'application/json');
    headers.set('Content-Type', 'application/json');

    if(!this.isGuestRoute(path)) {
        this.createAuthorizationHeader(headers);
    }

    return new RequestOptions({ headers: headers });
}

get(path: string) {
    return this._http.get(this.actionUrl + path, this.getHeaders(path))
        .map(res => res.json())
        .catch(this.handleError);
}
Chris Mayer
  • 187
  • 3
  • 10

1 Answers1

3

Alright, looked in the ionic docs and I do understand why you put them both underneath eachother since they also display it like that in the docs.

But Storage.set(key, value) :

Returns: Promise that resolves when the value is set

This means that you cannot use it the way you are using it (hence why they added a comment with //or ....

Since resolving a Promise is asynchronous.

If you want to use the value like you're currently using it (which seems a bit odd but probably for you to test if the value is set correctly) you should use

this.storage.set('api_token', data.token).then(() => {
   this.storage.get('api_token').then((value) => {
       console.log('storage : '+ value); // is empty...
    });
});

console.log('raw : ' + data.token); // shows the api token

If you would like some more information about why this happens, check out this SO answer (I prefer second one) Asynchronous vs synchronous execution, what does it really mean?

Community
  • 1
  • 1
Ivar Reukers
  • 7,560
  • 9
  • 56
  • 99
  • Thanks, that makes sense but still does not explain why the value is only available after reloading the app. I tested if the value is available with an event that I fired in the `storage.set` promise and the event simply called the `storage.get` and the value was available but when I navigate to the account page right after the login everything fails because the API Token is not available, if I refresh the app and then navigate to the account page everything is working fine. So am I still missing something or why is the value not populated throughout the whole application? – Chris Mayer Dec 07 '16 at 14:37
  • Do you push to the account page within the `storage.set().then()`? Because else the problem might again be the async execution. – Ivar Reukers Dec 07 '16 at 14:43
  • No, I send the user to the HomePage and then I manually navigate to the AccountPage where I call `this.userService.me()` which just sends an HTTP GET Request which at the moment fails if I don't reload the app because the API Token is not available no matter how long I wait. I navigate manually there to test if the data get properly loaded. – Chris Mayer Dec 07 '16 at 14:49
  • hmm weird, sounds more like a problem from the Storage, or an error in you `userService.me()` – Ivar Reukers Dec 07 '16 at 15:09
  • Well the `me` method simply returns `return this.http.get('http://api.com/users/me', bodyAndAuthHeader);` to which I subscribe and assign the data to the AccountPage user variable so nothing fancy happening there. The `Authorization` header of my request is always empty until I reload the app and then magically the `api_token` value is available. The `api_token` value is not available anywhere in the application until I reload it. – Chris Mayer Dec 07 '16 at 15:18
  • I have added some more code so it is apparent what I am trying to do – Chris Mayer Dec 07 '16 at 15:29