【问题标题】:Angular router guard and ROUTER_NAVIGATION effect orderAngular 路由器保护和 ROUTER_NAVIGATION 效果顺序
【发布时间】:2018-04-22 12:17:12
【问题描述】:

有一个简单的(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 action 触发的@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_NAVIGATIONeffect 执行顺序在警卫执行顺序之前。如何正确处理?

基于GitHub - state_management_ngrx4

【问题讨论】:

    标签: typescript ngrx ngrx-effects ngrx-store-4.0


    【解决方案1】:

    有一种方法可以实现它。您可以在效果中订阅 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.
    });
    

    【讨论】:

    • 如何在我的效果中订阅 Angular 路由器的 ResolveEnd 事件?您能否概述一些事件流或添加一些(伪)代码以首先实现警卫检查?谢谢
    • 您可以订阅路由器事件并为 Resolve End 过滤它并调度您自己的操作,称为 Router_Resove_End 操作等 this.router.events.filter(e => e instanceof ResolveEnd).subscribe( s => { // 在这里发送你自己的动作。});
    • > 您可以在效果中订阅 Angular 路由器的 ResolveEnd 事件。您可以修改代码 sn-p 并将@Effect() 添加到答案中吗? @Effect() routeChange$ = this.actions$.ofType(ROUTER_NAVIGATION) ... how to subscribe to resolve end in effect here ...
    • 嗨@Felix,我想解释一下,在你的效果类中,你必须自己订阅路由器事件。为此,您不需要@Effect()。您不应订阅 ROUTER_NAVIGATION 操作。这里的效果不会帮助你。另一个选项是不订阅 router_navigation 操作,并从您的 contractListGuard 调度一个 CONTRACT_LIST_LOADED 操作,您可以在效果中订阅它。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2017-10-14
      • 2021-12-16
      • 1970-01-01
      • 1970-01-01
      • 2017-03-10
      • 2019-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多