Angular router guard 和 ROUTER_NAVIGATION 效果命令
Angular router guard and ROUTER_NAVIGATION effect order
有一个简单的 (Angular 4) 路由守卫,它等待从后端加载一些数据:
@Injectable()
export class ContractsLoadedGuard implements CanActivate {
constructor(private store: Store<State>) { }
waitForData(): Observable<boolean> {
return this.store.select(state => state.contracts)
.map(contractList => !!contractList)
.filter(loaded => loaded)
.take(1);
}
canActivate(): Observable<boolean> { return this.waitForData(); }
}
路由:
const routes: Routes = [
{ path: 'app-list', canActivate: [ContractsLoadedGuard], component: AppListComponent },
];
最后还有一个由@ngrx/router-store v4 ROUTER_NAVIGATION
动作触发的@ngrx/effects:
@Effect() routeChange$ = this.actions$
.ofType(ROUTER_NAVIGATION)
.filter((action: RouterNavigationAction) => action.payload.routerState.url.indexOf('/app-list') > -1)
.withLatestFrom(this.store.select(state => state.contracts))
.switchMap(([action, contracts]: ([RouterNavigationAction, ContractList])) =>
this.restClient.load(action.payload.routerState.queryParams, contract));
不幸的是,当导航更改为 /app-list
时,ngrx 效果首先执行(在守卫之前),因此数据 state.contracts
尚不可用。 守卫还没有被处决
我必须添加 .combineLatest()
Rx 运算符来等待 contracts
数据生效(这是守卫的工作):
@Effect() routeChange$ = this.actions$
.ofType(ROUTER_NAVIGATION)
.filter((action: RouterNavigationAction) => action.payload.routerState.url.indexOf('/app-list') > -1)
.combineLatest(this.contractListGuard.waitForContractsToLoad(), a => a) <- HERE
.withLatestFrom(this.store.select(state => state.contracts))
.switchMap(/* same here */)
我不确定这个解决方案是否足够好。必须有更好的方法来做到这一点 - 不要复制有效的保护功能。
总结:在应用程序 boostrap 上,我需要从后端获取一些数据 - contracts
。如果用户导航到 /app-list
(立即重定向),则会从服务器获取其他数据 - 基于某些查询参数和 contracts
- ngrx 路由器 ROUTER_NAVIGATION
effect
执行顺序是在守卫执行命令之前。如何正确处理?
有一种方法可以实现。您可以在您的效果中订阅 Angular 路由器的 ResolveEnd 事件 https://angular.io/api/router/ResolveEnd,然后为 RESOLVE_END 发送您自己的操作,您可以在其中对您的解析器/防护数据进行操作。
实际上我在 ngrx/platform 中创建了一个 PR,其中 ngrx/router 将开箱即用地发送 NAVIGATE_RESOLVE_END 操作。我在等待 ngrx 团队接受我的 PR。 https://github.com/ngrx/platform/pull/524/
您可以订阅路由器事件并为 Resolve End 过滤它并发送您自己的操作调用它 Router_Resove_End 操作等。
this.router.events.filter(e => e instanceof ResolveEnd).subscribe(s => {
// dispatch your own action here.
});
Medium 的总结非常好:
@Injectable()
export class RouterEffects {
constructor(
private actions$: Actions,private router: Router,private location: Location,private store: Store<any>
) {this.listenToRouter();}
@Effect({ dispatch: false })
navigate$ = this.actions$.pipe(ofType('[Router] Go')...);
@Effect({ dispatch: false })
navigateBack$ = this.actions$.pipe(ofType('[Router] Back'), tap(() => this.location.back()));
@Effect({ dispatch: false })
navigateForward$ = this.actions$.pipe(ofType('[Router] Forward'),...);
private listenToRouter() {
this.router.events.pipe(
filter(event => event instanceof ActivationEnd)
).subscribe((event: ActivationEnd) =>
this.store.dispatch(new RouteChange({
params: { ...event.snapshot.params },
path: event.snapshot.routeConfig.path
}))
);
}
}
然后代替:
@Effect() routeChange$ = this.actions$.ofType(ROUTER_NAVIGATION)
使用:
@Effect() routeChange$ = this.actions$.ofType(ofType('[Router] Route Change'))
有一个简单的 (Angular 4) 路由守卫,它等待从后端加载一些数据:
@Injectable()
export class ContractsLoadedGuard implements CanActivate {
constructor(private store: Store<State>) { }
waitForData(): Observable<boolean> {
return this.store.select(state => state.contracts)
.map(contractList => !!contractList)
.filter(loaded => loaded)
.take(1);
}
canActivate(): Observable<boolean> { return this.waitForData(); }
}
路由:
const routes: Routes = [
{ path: 'app-list', canActivate: [ContractsLoadedGuard], component: AppListComponent },
];
最后还有一个由@ngrx/router-store v4 ROUTER_NAVIGATION
动作触发的@ngrx/effects:
@Effect() routeChange$ = this.actions$
.ofType(ROUTER_NAVIGATION)
.filter((action: RouterNavigationAction) => action.payload.routerState.url.indexOf('/app-list') > -1)
.withLatestFrom(this.store.select(state => state.contracts))
.switchMap(([action, contracts]: ([RouterNavigationAction, ContractList])) =>
this.restClient.load(action.payload.routerState.queryParams, contract));
不幸的是,当导航更改为 /app-list
时,ngrx 效果首先执行(在守卫之前),因此数据 state.contracts
尚不可用。 守卫还没有被处决
我必须添加 .combineLatest()
Rx 运算符来等待 contracts
数据生效(这是守卫的工作):
@Effect() routeChange$ = this.actions$
.ofType(ROUTER_NAVIGATION)
.filter((action: RouterNavigationAction) => action.payload.routerState.url.indexOf('/app-list') > -1)
.combineLatest(this.contractListGuard.waitForContractsToLoad(), a => a) <- HERE
.withLatestFrom(this.store.select(state => state.contracts))
.switchMap(/* same here */)
我不确定这个解决方案是否足够好。必须有更好的方法来做到这一点 - 不要复制有效的保护功能。
总结:在应用程序 boostrap 上,我需要从后端获取一些数据 - contracts
。如果用户导航到 /app-list
(立即重定向),则会从服务器获取其他数据 - 基于某些查询参数和 contracts
- ngrx 路由器 ROUTER_NAVIGATION
effect
执行顺序是在守卫执行命令之前。如何正确处理?
有一种方法可以实现。您可以在您的效果中订阅 Angular 路由器的 ResolveEnd 事件 https://angular.io/api/router/ResolveEnd,然后为 RESOLVE_END 发送您自己的操作,您可以在其中对您的解析器/防护数据进行操作。
实际上我在 ngrx/platform 中创建了一个 PR,其中 ngrx/router 将开箱即用地发送 NAVIGATE_RESOLVE_END 操作。我在等待 ngrx 团队接受我的 PR。 https://github.com/ngrx/platform/pull/524/
您可以订阅路由器事件并为 Resolve End 过滤它并发送您自己的操作调用它 Router_Resove_End 操作等。
this.router.events.filter(e => e instanceof ResolveEnd).subscribe(s => {
// dispatch your own action here.
});
Medium 的总结非常好:
@Injectable()
export class RouterEffects {
constructor(
private actions$: Actions,private router: Router,private location: Location,private store: Store<any>
) {this.listenToRouter();}
@Effect({ dispatch: false })
navigate$ = this.actions$.pipe(ofType('[Router] Go')...);
@Effect({ dispatch: false })
navigateBack$ = this.actions$.pipe(ofType('[Router] Back'), tap(() => this.location.back()));
@Effect({ dispatch: false })
navigateForward$ = this.actions$.pipe(ofType('[Router] Forward'),...);
private listenToRouter() {
this.router.events.pipe(
filter(event => event instanceof ActivationEnd)
).subscribe((event: ActivationEnd) =>
this.store.dispatch(new RouteChange({
params: { ...event.snapshot.params },
path: event.snapshot.routeConfig.path
}))
);
}
}
然后代替:
@Effect() routeChange$ = this.actions$.ofType(ROUTER_NAVIGATION)
使用:
@Effect() routeChange$ = this.actions$.ofType(ofType('[Router] Route Change'))