I have an existing project developed with Angular 4. I need to control the access to a particular route based on user-rights. The simplified route configuration looks like this:
[
{ path: '', redirectTo: '/myApp/home(secondary:xyz)', pathMatch: 'full' },
{ path: 'myApp'
children: [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', ... },
...
{ path: 'product'
children: [
{ path: '', redirectTo: 'categoryA', pathMatch: 'full' },
{ path: 'categoryA', component: CategoryAComponent, canActivate: [CategoryACanActivateGuard]},
{ path: 'categoryB', component: CategoryBComponent},
...
]
},
...
]
},
...
]
Now, I want to control the access to www.myWeb.com/myApp/product/categoryA
. If the user doesn't have enough permission, he/she will be redirected to ... /product/CategoryB
. I have written a CanActivate
RouteGuard to do this, the guard class looks like this:
import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot, ActivatedRoute } from '@angular/router';
import { MyService } from '... my-service.service';
@Injectable()
export class CategoryACanActivateGuard implements CanActivate {
constructor(private myService: MyService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return this.myService.checkPermission()
.then(result => {
if (!result.hasAccess) {
//redirect here
this.router.navigate(['./myApp/product/categoryB']);
//works, but I want to remove hardcoding 'myApp'
//this.router.navigate(['../../categoryB']);
//doesn't work, redirects to home page
//this.router.navigate(['./categoryB'], { relativeTo: this.route});
//do not have this.route object. Injecting Activated route in the constructor did not solve the problem
}
return result.hasAccess;
});
}
}
Everything works fine, but I want redirect relative to the target route like the following:
this.router.navigate(['/product/categoryB'], { relativeTo: <route-of-categoryA>});
// or
this.router.navigate(['/categoryB'], { relativeTo: <route-of-categoryA>});
Unfortunately, relativeTo
accepts only ActivatedRoute
objects and all I have is ActivatedRouteSnapshot
and RouterStateSnapshot
. Is there a way to navigate relative to the target route (in this case categoryA)? Any help will be really appreciated.
Note:
this.router.navigateByUrl
using state.url
. I want to use router.navigate([...], { relativeTo: this-is-what-need})
.It turns out, constructor injected ActivatedRoute
works differently in RouteGuard compare to other places like a Component.
In a component, ActivatedRoute
object points to the route that activated that component. For example, in the CategoryAComponent
class, the following code will navigate to CategoryB:
this.router.navigate([ '../CategoryB' ], { relativeTo: this.route });
But, the same code above will not work in a RouteGuard
class added to the CategoryA route configuration. In my test, I have found that the constructor injected ActivatedRoute
object points to the root route. On the other hand, ActivatedRouteSnapshot
object (passed in as a parameter in the canActivate function) points to the target route (in my case categoryA). However, we cannot pass this ActivatedRouteSnapshot
object in this.router.navigate(...)
function.
I couldn't find a better way to solve this problem, but the following code works for me:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return this.myService.checkPermission()
.then(result => {
if (!result.hasAccess) {
//redirect here
let redirectTo = route.pathFromRoot
.filter(p => p !== route && p.url !== null && p.url.length > 0)
.reduce((arr, p) => arr.concat(p.url.map(u => u.path)), new Array<string>());
this.router.navigate(redirectTo.concat('categoryB'), { relativeTo: this.route });
}
return result.hasAccess;
});
}