Search code examples
angularrxjsobservableangular-routerangular-guards

Angular 4 call an observable after the first finish then return a value


I have a big problem and after hours of reading documents and solution I can't find how I will be able to solve this problem :

Basically I have in my angular guard this :

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        return this.checkLogin(route);
    }

    checkLogin(route) {
        return this.authService.login().map(response => {
            if (this.authService.isAuth) {
                return this.authService.grants().map(grants => {
                    if (grants.canRead) {
                        return true;
                    }
                })
            }
        });
    }

What I want to do is first to call the service authService.login, and if I'm authentified then I call the authService.grants to check if the user can read this page or not.

As simple as that but I'm not being able to get into the second call I don't know why it's returning an observable.


Solution

  • Without having the definitions of your methods, Ill assume the following:

    interface Rights {
       canRead: boolean;
    }
    
    class AuthService { 
      grants(): Observable<Rights>{}
      login(): Observable<any>{}
    }
    

    The problem is, that currently the return type of your checkLogin method is Observable<boolean>| Observable<Observable<boolean>>. This is because:

    • If this.authService.isAuth is false, the outer map will yield a Observable<boolean>, as you simply return false.
    • If this.authService.isAuth is true, the outer map will yield a Obsevable<Observable<boolean>>, as you will now return the mapped values of another async. operation.

    So using that, you should refactor your guard to the following:

    import 'rxjs/add/operator/mergeMap'; // can be refactored to pipe + operator
    import {of} from 'rxjs/observable/of';
    
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
            return this.checkLogin(route);
        }
    
        checkLogin(route): Observable<boolean> {
            return this.authService.login().mergeMap(_=> {
                if (this.authService.isAuth) {
                    return this.authService.grants().map(grants => grants.canRead)
                }else{
                    return of(false);
                }
            });
        }
    }
    

    MergeMap is basically allowing you to concatenate asynchronous operations, and of creates an observable of a sync. value.