0

I need to wait until the getPermissions() observable is complete before calling the checkPermissions() method. But for the life of me I can't get it...

I've tried using async/await too but that doesn't seem to work for me either?

I need to have my permissions, before I can check them, right. Thoughts?

If there's a better way, I'm all ears.

Much appreciated.

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable()
export class RoleGuardService implements CanActivate {
  
  constructor(public auth: AuthService,public router: Router) { }

  canActivate(route: ActivatedRouteSnapshot): boolean {
    //This will be passed from the route config, on the data property
    const expectedRole = route.data.expectedRole;
    var hasPerm = this.loadAndCheckPermissions(expectedRole);

    console.log('done! ' + hasPerm);
    return hasPerm;
  }

  loadAndCheckPermissions(expectedRole) {
    var hasPermission = false;
    localStorage.clear();
    var myPermissions = localStorage.getItem('user-permissions');
    
    if (myPermissions === null) {
      console.log("PERMISSIONS from Server!");

      //You can't wait for an Observable or Promise to complete.
      //You can only subscribe to it to get notified when it completes or emits an event.
      this.auth.getPermissions()
        .subscribe(res => {
          localStorage.setItem('user-permissions', JSON.stringify(res.body));

          //Check permissions now
          //hasPermission = this.checkPermissions(expectedRole);
        });
    } else {
      hasPermission = this.checkPermissions(expectedRole); 
    }

    console.log('loadAndCheckPermissions ' + hasPermission);
    return hasPermission;
  }

  //Check a permission here
  checkPermissions(expectedRole) {
    return this.auth.checkPermission(expectedRole);
  }
}
rhavelka
  • 2,283
  • 3
  • 22
  • 36
Mark
  • 2,543
  • 6
  • 33
  • 44
  • `loadAndCheckPermissions` should be and observable, same with `canActivate`. If you have higher functions that are dependent on what they return, you will either need to combine functions or keep declaring child functions as observables – rhavelka Jul 11 '18 at 21:51

1 Answers1

1

You can return a Observable in you canActivate method, like that:

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable()
export class RoleGuardService implements CanActivate {

  constructor(
    public auth: AuthService,
    public router: Router
  ) { }

  canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {

    //This will be passed from the route config, on the data property
    const expectedRole = route.data.expectedRole;

    var hasPerm = this.loadAndCheckPermissions(expectedRole);

    console.log('done! ' + hasPerm);
    return hasPerm;
  }

  loadAndCheckPermissions(expectedRole):  Observable<boolean> {

    var hasPermission: Observable<boolean> = Observable.of(false);

    //clear
    localStorage.clear();

    //getter
    var myPermissions = localStorage.getItem('user-permissions');
    //
    if (myPermissions === null) {

      console.log("PERMISSIONS from Server!");

       hasPermission = this.auth.getPermissions()
        .map(res => {
          localStorage.setItem('user-permissions', JSON.stringify(res.body));
          console.log('return from async');

          // Check permissions RETURNING A BOOLEAN
          return this.checkPermissions(expectedRole);
        });

    }
    else {
      hasPermission = Obsersable.of(this.checkPermissions(expectedRole)); 
    }

   // console.log('loadAndCheckPermissions ' + hasPermission);
    return hasPermission;
  }

  //Check a permission here
  checkPermissions(expectedRole) {
    return this.auth.checkPermission(expectedRole);
  }


}

Accordingly to documentation of Angular, a routing guard can return an Observable or a Promise and the router will wait for the observable to resolve to true or false.

https://angular.io/guide/router#milestone-5-route-guards

Rodrigo
  • 2,313
  • 2
  • 15
  • 23
  • I don't think slapping on an `Observable` will solve the problem. You need to declare an `observer` and use `observer.next()` to return the values, like the examples [here](https://github.com/ReactiveX/rxjs/blob/master/doc/observable.md) – rhavelka Jul 11 '18 at 21:57
  • Theses examples are not using the Observable of the Angular API that neither has the next method. – Rodrigo Jul 11 '18 at 22:10
  • This doesn't work either as both the done! and loadAndCheckPermissions console.log() return before the async call is done. – Mark Jul 11 '18 at 22:33
  • Why do you need to wait the async call? If you want just implement a canActivate guard, it can return a Observable as result of an async call because the router will wait for the observable to resolve to true or false. The route won't be activated until the Observable (async call) finish. – Rodrigo Jul 11 '18 at 22:56
  • Mark, see this answer: https://stackoverflow.com/questions/37948068/angular-2-routing-canactivate-work-with-observable – Rodrigo Jul 11 '18 at 23:15