0

I am using Ionic2 Storage to store user access_token credentials when a user logs in.

When the user tries to access a backend api I need to supply the access_token to a REST client.

I have created service using hBoylan's ng2-http package Initially the package uses

export class CategoryService extends RESTClient{
    categoryList : Category[] = [];
    public user:User;
    constructor(protected http:Http) {super(http)}
}

I have used Angular2 DI to add a UserStorage component to the constructor:

let categoryServiceFactory = (http:Http, userStorage:UserStorage) => {
    return new CategoryService(http, userStorage);
}
export let categoryServiceProvider = 
{
    provide: CategoryService,
    useFactory: categoryServiceFactory,
    deps: [Http, UserStorage]
}

to get this:

export class CategoryService extends RESTClient{
  categoryList : Category[] = [];
  public user:User;
  constructor(protected http: Http, protected userStorage: UserStorage)
{
  super(http);
  this.userStorage.getUser().then((data:User) => {this.user = data})
}

Currently when I call

 @GET('/itemtype/list')
 @Produces<String>()
 public getAvailableCategories(): Observable<String> {
   return null;
 }

I need to use

 protected requestInterceptor(req: Request) {
   req.headers.set('Authorization', `Bearer ${this.user.api_credentials.access_token}`)
    return req;
 }

to add the access_token credential

as of right now my call in my page component has to look like this:

@Component({
  selector: 'page-categories',
  templateUrl: 'categories.html',
  providers: [User]
})
export class CategoriesPage implements OnInit {
  // list of categories
  public categories: any;

constructor(public nav: NavController,
public categoryService: CategoryService,
public userStorage: UserStorage,
public user: User) {}

ngOnInit() {
  this.userStorage.getUser().then((user: User) => {
    this.user = user
    this.categoryService.setUser(this.user);
    this.categoryService.getAvailableCategories().subscribe(
      (data) => { console.log(data) },
      error => console.log(error),
      () => { });
  });
}

// view category
viewCategory(categoryId) {
  this.nav.push(CategoryPage, { id: categoryId });
}

showChildren(category: Category) {
  category.open = !category.open;
}

openCategoryPage($event, category: Category) {
  $event.stopPropagation();
  this.nav.push(CategoryPage, { cat_id: category.id })
}

}

The reason it looks like that is because I CANNOT seem to set the user in the constructor of the CategoryService class. So I have to first get the User from storage, use a "setter" in the category service and then nest the getAvailableCategories() call inside of the getUser().then() function.

That is because the getCategories function is called before the CategoryService constructor is done setting the user from UserStorage.

I know this is because the call is asynchronous but I feel as though using something like setTimeout to "wait" until the getUser call has returned is hacky and not 100% dependable.

Is there anything else I can try? I would really like to just call

this.categoryService.getAvailableCategories().then()

and be continue on from there without the nested promise calls.

I am up for making any savvy changes that would make the access_token available right away.

Schwoebel
  • 216
  • 1
  • 4
  • 19

1 Answers1

1

If getUser call is asynchronous, you can try making it synchronous :)

If that's not an option for you, there's a syntax sugar which will help you deal with async calls in a nicer way – async/await:

async ngOnInit() {
    this.categories = this.categoryService.getCategories();
    this.user = await this.userStorage.getUser();
    this.categoryService.setUser(this.user);
    this.categoryService.getAvailableCategories().subscribe(
        (data) => { console.log(data) },
        (error) => { console.log(error) },
        () => { }
    );
}

Mind that it's async ngOnInit and then await this.userStorage.getUser()

Michał Miszczyszyn
  • 11,835
  • 2
  • 35
  • 53
  • I like this idea, and it solves my nesting nitpicking, but I am looking for something to use in the CategoryService so I don't have to call categoryService.setUser(). I would like help to figure out a solution so the CategoryServices's user variable will be readied in the background while Angular loads the page. – Schwoebel Feb 13 '17 at 11:34
  • @Schwoebel It's absolutely possible to do that in pure TypeScript/JavaScript. It's just that `hBoylan's ng2-http package` decorators apparently don't allow asynchronous parameters. You should open an issue on GitHub. – Michał Miszczyszyn Feb 13 '17 at 12:54
  • @Schwoebel Was my answer helpful to you in any way? – Michał Miszczyszyn Mar 28 '17 at 23:17