I am trying to implement a secure route based on the right of the user. A user who doesn't have a specific right can't access that route. That's why I am passing the Right Name in a route (lazy module routing). But, I found that it's a bit complicated to get lazy route data. I had to subscribe to router events. But, after subscribing I am not finding a way to return false or true to Canactivate.
Here is my canActivate code:
canActivate() {
this.router.events
.pipe(
filter((event) => event instanceof NavigationEnd),
map(() => this.activatedRoute),
map((route) => {
while (route.firstChild) {
route = route.firstChild;
}
return route;
}),
mergeMap((route) => route.data))
.subscribe((event) => {
const right = event.right; // getting the right name from the route data.
const rights = this.localStorageService.getLocalStorageItem('rights');
if (right) {
if (rights.includes(right)) {
// I need to return true from here
} else {
// I need to return false from here
}
}
});
}
And, this is my route code:
const routes: Routes = [{ path: ':id', component: ProfileComponent,
data: { right: 'VIEW_PROFILE' }, canActivate: [RightRouteGuardService]}];
An Angular route guard can return Observable<boolean> | Promise<boolean> | boolean
. I think you want to return a boolean but if I were you in this case, I would return Observable<boolean>
.
Try this:
canActivate(): Observable<boolean> {
return this.router.events
.pipe(
filter((event) => !!event), // !! add this filter !!
filter((event) => event instanceof NavigationEnd),
map(() => this.activatedRoute),
map((route) => {
while (route.firstChild) {
route = route.firstChild;
}
return route;
}),
mergeMap((route) => route.data),
// map will return true false for us
map(event => {
const right = event.right; // getting the right name from the route data.
const rights = this.localStorageService.getLocalStorageItem('rights');
if (right) {
if (rights.includes(right)) {
return true;
} else {
return false;
}
}
}),
);
}
You don't need to subscribe anymore, just the first emission from this observable will return the true and false for you.
========================= Edit ==========================
Don't inject the router and listen to the events because the router has not completed navigation yet and your filter
of NavigationEnd
will not allow it to proceed as well as there are no events at that moment in time.
I have solved the problem like so:
import { Injectable } from "@angular/core";
import {
ActivatedRouteSnapshot,
CanActivate,
RouterStateSnapshot
} from "@angular/router";
@Injectable({
providedIn: "root"
})
export class RightRouteGuardService implements CanActivate {
constructor() {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean{
let right = route.data.right;
console.log({ right });
const rights = ['VIEW_STUDENT'];
if (rights.includes(right)) {
return true;
} else {
return false;
}
}
}
The canActivate
method takes two properties and you can use the properties to read the data property of the route in question.
I got inspired by this post.