Search code examples
javascriptangulartypescriptrxjsngrx

Chain two selects rxjs store in Angular Guard


I'm trying to select two fields from the ngrx store in an Angular Guard like this:

@Injectable()
export class RoleGuard implements CanActivate {

  constructor(
    public router: ActivatedRouteSnapshot,
    private store: Store<AppState> ) {}

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

    const expectedRole = route.data.Role;

    return combineLatest(
        this.store.pipe(select(isLoggedIn)),
        this.store.pipe(select(currentUser)),
      ).pipe(
          tap( ([loggedIn, user]) => 
                {
                    if ( loggedIn && !(user.role.find(expectedRole) >= 0) ) {
                        this.router.navigateByUrl('/error/403')
                    };
                }
            )
        );


  }

}

However, I'm getting Type 'boolean | [any, any]' is not assignable to type 'boolean', which makes sense, since the combineLatest return the result in array. But I can't seem to find a more elegant way than combineLatest, instead of nesting the two select observable, what would be my alternatives here?


Solution

  • canActivate method should return a boolean wrapped in an Observable. As per your code, it is returning the values wrapped in an Observable returned from the combineLatest method which is an array. You can use map operator to return true or false like this:

    @Injectable()
    
    export class RoleGuard implements CanActivate {
    
      constructor(
        public router: ActivatedRouteSnapshot,
        private store: Store<AppState> ) {}
    
      canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    
        const expectedRole = route.data.Role;
    
        return combineLatest(
            this.store.pipe(select(isLoggedIn)),
            this.store.pipe(select(currentUser)),
          ).pipe(
              map( ([loggedIn, user]) => 
                    {
                        if ( loggedIn && !(user.role.find(expectedRole) >= 0) ) {
    this.router.navigateByUrl('/error/403')
                          //I am assuming that you want to fail the guard and want your application to route to 403 page, so let’s return false
                          return false;
                        };
    
                      //I am assuming that you want to pass the guard of above if condition fails, so return true; bottom line is to return true/false as per your logic.
                      return true;
    
                    }
                )
    
    );
      }
    
    }
    

    Hope it helps.