ramon
ramon

Reputation: 35

working with synchronous observables in javascript

this question is somehow related to this questions 1,2 , but they do not addressed the subject of synchronous observables in javascript in a manner that can help me.

I'm currently in angular 4 or just angular and i have a protected route waiting for two guards to resolve. the two of them returns true as expected but the second one takes too long so it's answer comes after the canActivate method has finished. example code below. what can i do to wait for my second guard to synchronously resolved?

I'm new to this javascript and observables async way of doing things! Sorry if this question is silly.

  1. first authGuard:

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): 
    boolean {
         if(this.cookieService.check('token'))return true;
         console.log(true); //For demostration purpose
    }
    
  2. second authGuard:

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): 
    boolean {
         this.roleService.checkRole('admin') // this service take some time
         .subscribe(
             response=>{
                 //Simple logic checking the response.
                 console.log(true); 
                 return true;
             },
             error=>{
                 //some logic with the error
                 console.log(false);
                 return false;
             }
    
         )
    console.log('End of authguard method');
    }
    
  3. roleService:

    checkRole(role:string): 
    boolean {
           return  this.http.post(Url, body,requestOptions)
                            .map(this.processData)
                            .catch(this.handleError);
    }
    

4.Console shows:

    true                        // from authGuard 1
    "End of authguard method"   // from authGuard 2
    true                        // from authGuard 2

The router can not navigate to the desire route because of the second true coming too late. I also have tried the first() operator before subscribing and the first operator do not wait for the observable in the role.Service to resolve.

Upvotes: 1

Views: 961

Answers (1)

paulpdaniels
paulpdaniels

Reputation: 18663

The interface for CanActivate is

interface CanActivate { 
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>|Promise<boolean>|boolean
}

Which means that it will accept a return type of a Promise or and Observable in addition to a raw boolean value. The issue that you are facing is that you are trying to treat an Observable as though it is a boolean value, which it is not. You should instead refactor your second authGuard to be:

canActivate(
  route: ActivatedRouteSnapshot, 
  state: RouterStateSnapshot
): Observable<boolean> {
  return this.roleService
     .checkRole('admin') // this service take some time
     // Any success value is mapped to true
     .mapTo(true)
     // Any error will log and return false
     .catch(e => {
       console.log('Encountered an error checking role', e);
       return Observable.of(false);
     })
     // This logs when this *actually* completes
     .finally(() => console.log('End of authguard method'));
}

The reason we need to do the above is because as a general pattern we don't know when an Observable will emit, we just know what we want to do when it does. Instead of trying to assign a single value to return, we are actually returning a pipeline of work that will be accomplished eventually.

Upvotes: 1

Related Questions