Use tap() instead of map() RxJS and Angular
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.
panagulis72
Updated on June 08, 2022Comments
-
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:
- I open the app
- I'm not logged in
- The url becomes http://localhost:4200/#/ instead of http://localhost:4200/#/login, result: blank page
- If I try to go manually in http://localhost:4200/#/login, it stays in that page but I see a blank page
- If I try to go home, it redirects on http://localhost:4200/#/ instead of http://localhost:4200/#/login
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 almost 6 yearsbut you may seperate the routing into a tap (after the map) :-)
-
jonrsharpe almost 6 yearsThat 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 almost 6 yearsSame comment: stackoverflow.com/questions/51205810/…
-
panagulis72 almost 6 yearsSo 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 almost 6 yearstap 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 almost 6 yearsHi 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.