Angular: 如何在 RouteGuard 中相对于目标路由调用 router.navigate()?
Angular: how to call router.navigate() relative to target route in a RouteGuard?
我有一个使用 Angular 开发的现有项目 4. 我需要根据用户权限控制对特定路径的访问。简化的路由配置如下所示:
[
{ 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},
...
]
},
...
]
},
...
]
现在,我想控制对 www.myWeb.com/myApp/product/categoryA
的访问。如果用户没有足够的权限,he/she 将被重定向到 ... /product/CategoryB
。我写了一个 CanActivate
RouteGuard 来做这个,守卫 class 看起来像这样:
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;
});
}
}
一切正常,但我想要相对于目标路由的重定向,如下所示:
this.router.navigate(['/product/categoryB'], { relativeTo: <route-of-categoryA>});
// or
this.router.navigate(['/categoryB'], { relativeTo: <route-of-categoryA>});
不幸的是,relativeTo
只接受 ActivatedRoute
个对象,而我只有 ActivatedRouteSnapshot
和 RouterStateSnapshot
。有没有办法相对于目标路线进行导航(在本例中为 categoryA)?任何帮助将不胜感激。
注:
- 除了添加一些路由守卫外,我无法更改路由配置。
- 我不是在寻找
this.router.navigateByUrl
使用 state.url
。我想使用 router.navigate([...], { relativeTo: this-is-what-need})
.
事实证明,构造函数注入 ActivatedRoute
在 RouteGuard 中的工作方式与 Component 等其他地方不同。
在组件中,ActivatedRoute
对象指向激活该组件的路由。例如,在 CategoryAComponent
class 中,以下代码将导航到 CategoryB:
this.router.navigate([ '../CategoryB' ], { relativeTo: this.route });
但是,上面的相同代码在添加到 CategoryA 路由配置的 RouteGuard
class 中不起作用。在我的测试中,我发现注入的构造函数 ActivatedRoute
对象指向根路由。另一方面,ActivatedRouteSnapshot
对象(在 canActivate 函数中作为参数传入)指向目标路由(在我的例子中是 categoryA).但是,我们不能在 this.router.navigate(...)
函数中传递这个 ActivatedRouteSnapshot
对象。
我找不到更好的方法来解决这个问题,但以下代码对我有用:
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;
});
}
我有一个使用 Angular 开发的现有项目 4. 我需要根据用户权限控制对特定路径的访问。简化的路由配置如下所示:
[
{ 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},
...
]
},
...
]
},
...
]
现在,我想控制对 www.myWeb.com/myApp/product/categoryA
的访问。如果用户没有足够的权限,he/she 将被重定向到 ... /product/CategoryB
。我写了一个 CanActivate
RouteGuard 来做这个,守卫 class 看起来像这样:
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;
});
}
}
一切正常,但我想要相对于目标路由的重定向,如下所示:
this.router.navigate(['/product/categoryB'], { relativeTo: <route-of-categoryA>});
// or
this.router.navigate(['/categoryB'], { relativeTo: <route-of-categoryA>});
不幸的是,relativeTo
只接受 ActivatedRoute
个对象,而我只有 ActivatedRouteSnapshot
和 RouterStateSnapshot
。有没有办法相对于目标路线进行导航(在本例中为 categoryA)?任何帮助将不胜感激。
注:
- 除了添加一些路由守卫外,我无法更改路由配置。
- 我不是在寻找
this.router.navigateByUrl
使用state.url
。我想使用router.navigate([...], { relativeTo: this-is-what-need})
.
事实证明,构造函数注入 ActivatedRoute
在 RouteGuard 中的工作方式与 Component 等其他地方不同。
在组件中,ActivatedRoute
对象指向激活该组件的路由。例如,在 CategoryAComponent
class 中,以下代码将导航到 CategoryB:
this.router.navigate([ '../CategoryB' ], { relativeTo: this.route });
但是,上面的相同代码在添加到 CategoryA 路由配置的 RouteGuard
class 中不起作用。在我的测试中,我发现注入的构造函数 ActivatedRoute
对象指向根路由。另一方面,ActivatedRouteSnapshot
对象(在 canActivate 函数中作为参数传入)指向目标路由(在我的例子中是 categoryA).但是,我们不能在 this.router.navigate(...)
函数中传递这个 ActivatedRouteSnapshot
对象。
我找不到更好的方法来解决这个问题,但以下代码对我有用:
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;
});
}