3

I guess this might more of a rxjs understanding topic but the context illustrates my want of it the best :)

So below it the working code inside my PageGuard class, which prevents routing to pages unless a valid jwt exists in localStorage.

CheckForToken() just appends Authorization Header if token exists in localStorage.

public isAuthenticated():Observable<boolean>{
    this.checkForToken();
    let isAuth = new Observable<boolean>(observer => {
        this.http.get(`https://testhan-api.selfbits.io/api/v1/user`,{headers: this.headers}).subscribe(res => {
            if (res.status === 200){
                observer.next(true);
                observer.complete();
            }else{
                observer.next(false);
                observer.complete()
            }
        },err => console.log(err));
    });
    return isAuth
}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return this.isAuthenticated()
}

and the routing path looks like this

{ 
    path:'dashboard', 
    component:DashboardComponent, 
    canActivate:PageGuard
}

My question: So far, my understanding is that you need to subscribe to an observable, in order to execute it, like

observable.subscribe(res => //do something with res)

but here I'm only returning an observable, it doesn't get subscribed, but how does the guard evaluate it?

thanks for the clarification!

Han Che
  • 8,239
  • 19
  • 70
  • 116

1 Answers1

4

If you really wanna get down to the nitty gritty... here goes :-)

Most of the work regarding navigation goes on the runNavigate method. The point where it gets to the guards is here, where there's a call to checkGuards which returns an Observable<boolean>. Depending on what type of guard it is, it will call a specific method for that type of guard, that also returns an Observable<boolean>. All of those "specific methods" call a method wrapIntoObservable passing in the result of the guard method. If you look at the wrapIntoObservable source, you will see that it checks for a Observable, a Promise, or a regular value. In either case, an Observable is always returned.

Now going back down the call stack, once all the guard Observables are all merged into an Observable emitting a single boolean value, that observable goes through the process of resolving data for the routes. If the merged guard observable returns false, the new resolve Observable will return an Observable of false. Otherwise it will return an Observable of the resolved data.

In the next step, the resolve Observable gets subscribed to with forEach. If the resolved data is false (false forwarded from the guards), then it will return nothing and the routing stops. Otherwise, anActivatedRoute. From there the rest of the navigation continues.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • I note that you say "... once those Observables are all resolved ...", but could you emphasise that the Observables must complete? That seems to catch out quite a few people. – cartant Sep 22 '16 at 06:10
  • 1
    @cartant I made it a little more accurate. All the guard observables get merged with `mergeMap` into a single boolean emitting observable :-) – Paul Samsotha Sep 22 '16 at 06:14
  • thanks a lot for the detailed explanation!!! So basically the observable I've created above is sufficient as well as the way I'm returning it in canActivate? – Han Che Sep 22 '16 at 07:37
  • @HanChe You can return an Observsble or a Promise, or a normal value – Paul Samsotha Sep 22 '16 at 07:47