5

有一个简单的(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 v4ROUTER_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-listngrx 效果时首先执行(在警卫之前),因此数据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执行顺序在警卫执行顺序之前。如何正确处理?

基于GitHub - state_management_ngrx4

4

2 回答 2

2

有一种方法可以实现它。您可以在效果中订阅 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.
});
于 2017-11-09T20:48:30.993 回答
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'))
于 2018-03-05T13:22:55.220 回答