Use tap() instead of map() RxJS and Angular

10,708

Solution 1

This is the signature of canActivate:

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

canActivate must either return boolean or Promise. Therefore you must map it and return a boolean or Promise. tap will not do the trick.

Solution 2

The guard canActivate has to return an Observable<boolean> in order for the Angular guard mechanism to work.

Using map allows you to define a return which is different from what you get from isAuthenticated, and this probably explains what you see.

In your first version canActivate acutally returns an Observable<boolean> - in the second case it returns this this.store.select(fromApp.isAuthenticated).pipe(take(1)) , and probably it is not what you want.

Share:
10,708
panagulis72
Author by

panagulis72

Updated on June 08, 2022

Comments

  • panagulis72
    panagulis72 almost 2 years

    I have a login guard which basically check if the user il logged in. If he is logged, it skips the login and go to home page. I wrote this code, and this works:

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        return this.store.select(fromApp.isAuthenticated)
            .pipe(take(1),
                map(isAuthenticated => {
                    if (isAuthenticated) {
                        this.router.navigate(['/home']);
                        return false;
                    } else {
                        return true;
                    }
                })
            )
    }
    

    Now, since I don't have to change or edit the output data (isAuthenticated boolean), I thought: well, why don't use tap operator? So, I re-write my code:

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        return this.store.select(fromApp.isAuthenticated)
            .pipe(take(1),
       HERE------> tap(isAuthenticated => {   <------ HERE
                    if (isAuthenticated) {
                        this.router.navigate(['/home']);
                        return false;
                    } else {
                        return true;
                    }
                })
            )
    }
    

    Then I went to Chrome, and I saw black page and this was the case:

    In any case, I see blank page. So, I went in debug and I noticed that it correctly jump inside the if/else block... so, why tap() is breaking the app?

  • JanRecker
    JanRecker almost 6 years
    but you may seperate the routing into a tap (after the map) :-)
  • jonrsharpe
    jonrsharpe almost 6 years
    That doesn't mean you must map it; what does store.select return? If that's already an observable of boolean then a tap is fine. I think it's a value problem, not a type problem.
  • jonrsharpe
    jonrsharpe almost 6 years
  • panagulis72
    panagulis72 almost 6 years
    So tap just continue the stream withouth return nothing? @JanRecker lol glad to see your help again :D what do you mean? Do dispatch a new action which works with tap() and manages the routing?
  • siva636
    siva636 almost 6 years
    tap will not modify the stream, it is used to put side effects in it, such as console logs for example (for trouble shooting purpose).
  • JanRecker
    JanRecker almost 6 years
    Hi again :-) in my personal rxjs religion (as is said, very personal) i extract everything that is not altering the stream into "taps". My reason is, that i like "intentions" of code snippets. The intention of "map" is to alter the stream, the intention of tap is to handle sideeffects. There is no need do it like that, but i find it more readable. About the Action, i like the idea. if you have any clean ups to do, the action can nicely handle it. If not, ... The new URL should be a state change by itself.