首页 文章

角度路由器防护和ROUTER_NAVIGATION效果顺序

提问于
浏览
2

有一个简单的(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 /效果:

@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 尚未可用 . The guard has not been executed yet. 我必须添加 .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 执行顺序位于保护执行顺序之前 . 如何妥善处理?

基于GitHub - state_management_ngrx4

2 回答

  • 1

    有一种方法可以实现它 . 您可以在效果中订阅Angular Router的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 action等 .

    this.router.events.filter(e => e instanceof ResolveEnd).subscribe(s => {
     // dispatch your own action here.
    });
    
  • 0

    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'))
    

相关问题