3

I am facing a very strange issue with Ionic Storage. I have a method that reads a value from the storage and returns an a promise containing an object corresponding to that:

private getAuthorizedOptions(): Promise<RequestOptions>
  {
        return this._storage.get('bdAccessToken')
            .then(v => {
                console.log("access token: ", v);
                let token = v;
                let header = new Headers({
                    'Authorization': 'Bearer ' + token
                });
                let ro = new RequestOptions({
                    headers: header
                });
                let options = new RequestOptions();
                if (options.headers) options.headers.delete("Authorization");
                options.headers = header;
                return options;
            });
  }

Now I have another method, which will call this above method twice within a chain of actions:

get(url:string, options?:RequestOptions): Observable<Response>
    {   
        return Observable.fromPromise(this.getAuthorizedOptions())
                .mergeMap((options) => 
                {
                    return super.get(url, options)
                        .catch(err => {
                            if (err && err.status === 401) 
                            {
                                return this._authService.refreshToken()
                                    .mergeMap(r => 
                                        {
                                            return Observable.fromPromise(this.getAuthorizedOptions())
                                                .mergeMap(opt => {
                                                    return super.get(url, opt)
                                            });

                                        }
                                    )
                                    .catch(err2 => {
                                        console.log("redirecting.");
                                        this.redirect();
                                        return Observable.throw(err2);
                                    });
                            }
                            else {
                                return Observable.throw(err);
                            }
                        });
                });
    }

Now tracing these methods shows something strange. The first time the "getAuthorizedOptions()" method is called, it can read the "bdAccessToken" value from the storage very well. The second time it is called, the returned value is NULL.

I have been pulling my hair for two days on this, any help is appreciated like you have never been appreciated before! lol!

Behrooz
  • 1,895
  • 4
  • 31
  • 47

1 Answers1

2

I had some issues with storage, and quirky behaviour, that ended up being related to asynchronous issues.

Things not executing in the desired/expected sequence.

So I ended up making my service stateful and monitor for a BehaviourSubject event instead.

import { Injectable }       from '@angular/core';
import { Storage }          from '@ionic/storage';
import { Server }           from './../model/server';
import { Subscription }     from 'rxjs/Subscription';
import { BehaviorSubject }  from "rxjs/BehaviorSubject";

export class LoginService {
  private static readonly SERVER = 'server';
  private servers$:BehaviorSubject<Server[]>;
  private servers: Server[];
  public serversSubs:Subscription

  constructor(public storage: Storage) {

    this.servers$ = new BehaviorSubject([] as Server[]);
    this.nextServersFromGetLocal(); // May need to be after subscribe.. Hot off presses.. 
    this.serversSubs = this.servers$.subscribe((servers:Server[]) =>
        this.servers = servers);
  }


  private nextServersFromGetLocal():void {
    this.storage.get(LoginService.SERVER).
      then( (value:string) =>  {
            this.servers$.next( JSON.parse(value) as Server[] );
                               }
      ).catch( ()           => {
             this.servers$.next( [] as Server[] );
                               }
      );
  }     

  private nextServersFromSetLocal(servers:Server[]): void {
    let data = JSON.stringify(servers);
    this.storage.set(LoginService.SERVER, data);
    this.servers$.next(servers);
  }

  getServers$(): BehaviorSubject<Server[]> {
    return this.servers$;
  }

  addServer(addServer:Server): void {
     // other code too...
     this.servers.push(addServer);
     this.nextServersFromSetLocal(this.servers);
  }

  changeServer(changeServer:Server): void {
    // other code too...
    this.nextServersFromSetLocal(this.servers);
  }

  deleteServer(deleteServer:Server): void {
    // other code too..
    this.nextServersFromSetLocal(this.servers);
  }
}

This refactoring had the added benefit of simplifying other code that did CRUD operations on the service, by not having to inline complex nested/repeating promise/then/catch blocks of code because of asynchronous behaviour. Hope this helps you.

You can sort of get the idea of how this worked within the context of my Ionic app here, as I posted a related question, and you can see the HTML view side of this in the screenshot

JGFMK
  • 8,425
  • 4
  • 58
  • 92
  • Thanks, can you by any chance help me to write an interceptor (capable of refreshing access token) using ionic storage? I have the same thing fully developed and functioning using localStorage, but I am stuck in making it work with an async storage. – Behrooz Aug 14 '17 at 15:44
  • @behroozdalvandi - I've been meaning to look more into interceptors within Angular stack, but have yet to get around to it. I know Angular University has a PDF that covers interceptors. – JGFMK Aug 14 '17 at 16:44