今天我遇到了同样的问题。不幸的是,我没有找到任何好的解决方案,所以我不得不自己创建一个。
这个想法是创建一个指令,它将路由器路径作为参数,如果不允许转换,则隐藏链接。例如:
<li [allowTransition]="'list'">
<a routerLink="list">List</a>
</li>
指令的逻辑如下:
1) 导入所有路由器的配置并从属性中找到输入路径的配置。我的配置如下:
{ path: 'login', component: LoginComponent },
{ path: 'list', component: ListComponent, canActivate: [UserGuardService]},
2) 然后该指令读取配置的 canActivate 属性并通过注入器获取 UserGuardService。通过这种方式,指令采用 UserGuardService 的实例。 UserGuardService 是实现 CanActivate 接口的类。
3)UserGuardService 知道是否允许转换。所以我们应该通过更改 css 显示属性来询问它并处理答案。
但是有两个特性对实现有很大影响:
A) CanActivate 接口不舒服,因为要调用它,我们必须创建 ActivatedRouteSnapshot 和 RouterStateSnapshot 对象。真正的过渡可能是必要的,但万一我们只想知道过渡是否可能。
B) 我们应该在某些事件之后提供链接可见性刷新(在我的例子中是登录事件)
解决问题 A 我已经创建了接口 Guard。
export interface Guard {
allowTransition():boolean;
}
所以,我希望我的所有 CanActivate 实现也实现 Guard 接口。现在我的典型 CanActivate 实现如下所示:
canActivate(route:ActivatedRouteSnapshot,
state:RouterStateSnapshot):Observable<boolean>|boolean {
//this method performs only "naigation" logic. It calls another method for business logic.
var allow = this.allowTransition();
if (!allow) {
this.router.navigate(['login']);
}
return allow;
}
allowTransition():boolean {
// this method contains business logic of transition possibility
return this.userService.hasCurrentUserRole("USER");
}
在这样的“接口划分”之后,我可以从我的指令中调用简单的方法 allowTransition()。代码如下。
import {routes} from "../../../app.routes";
... other imports
@Directive({selector: '[allowTransition]'})
export class AllowTransition implements OnDestroy, LoginListener, OnInit {
private el: HTMLElement;
private visibleDisplay:string;
@Input('allowTransition') destUrl: string;
constructor(el: ElementRef, private injector: Injector,
@Inject('appUserService') private userService : UserService ) {
this.el = el.nativeElement;
this.visibleDisplay = this.el.style.display;
}
ngOnDestroy() {
this.userService.removeLoginListener(this)
}
ngOnInit() {
this.userService.addLoginListener(this);
this.onLogin();
}
onLogin() {
let allow = true;
for (let i = 0; i < routes.length; i++) {
let path = routes[i].path;
if (path == this.destUrl) {
let canActivate = routes[i].canActivate;
if (canActivate != null) {
//todo do the same for canActivate[i]
try {
let canActivateInstance = this.injector.get(canActivate[0]) as Guard;
allow = canActivateInstance.allowTransition();
} catch (error) {
//did you forget to implement Guard?
console.error('Error');
}
}
}
}
this.el.style.display = allow ? this.visibleDisplay : 'none';
}
}
我已经提到了上面的问题 B,这就是为什么我的指令使用一种方法 onLogin() 实现 LoginListener 接口的原因。虽然指令存在,但它应该在事件生产者处注册并在发生变化时进行处理。在我的情况下,登录或注销后 UserService 调用所有指令的方法 onLogin() 并且每个链接都会刷新。
初始显示属性也可以有多个值,这就是指令必须将其保留为 'visibleDisplay' 属性的原因。
作为结论,现在可以在没有任何与路由对应的组件信息的情况下使用该指令。这种方法的主要缺点是它不适用于像 'view/:id' 这样的动态路由。在 CanActivate 方法中也不允许使用 Observale 代替布尔值。当然,可以修复它,但在我看来,我这样做的方式对于这么简单的事情来说已经太复杂了。我刚刚完成了所有我需要的工作,并等待具有类似功能的 angular2 更新。
更多信息:https://kosbr.github.io/2016/10/18/angular2-guard-router.html