繁体   English   中英

如何使用有角度的路由器防护装置连接 ngrx/store

[英]How to attach ngrx/store with an angular router guard

我是 ngrx/store 的初学者,这是我使用它的第一个项目。

我已经成功地使用 ngrx/store 设置了我的 angular 项目,并且在初始化我的主要组件后,我能够分派加载操作,如下所示:

ngOnInit() { this.store.dispatch({type: LOAD_STATISTICS}); }

我已经设置了一个效果来在调度此操作时加载数据:

@Effect()
loadStatistic = this.actions.ofType(LOAD_STATISTICS).switchMap(() => {
    return this.myService.loadStatistics().map(response => {
        return {type: NEW_STATISTICS, payload: response};
    });
});

我的减速器看起来像这样:

reduce(oldstate: Statistics = initialStatistics, action: Action): Statistic {
    switch (action.type) {
        case LOAD_STATISTICS:
            return oldstate;
        case NEW_STATISTICS:
            const newstate = new Statistics(action.payload);

            return newstate;
    ....

虽然这有效,但我无法理解如何将它与路由器防护一起使用,因为我目前只需要调度 LOAD_ACTION 一次。

此外,组件必须调度 LOAD 操作来加载初始数据对我来说听起来不合适。 我希望商店本身知道它需要加载数据,而我不必先分派动作。 如果是这种情况,我可以删除组件中的 ngOnInit() 方法。

我已经研究过 ngrx-example-app 但我还没有真正理解它是如何工作的。

编辑:

添加返回 ngrx-store observable 的解析保护后,路由不会被激活。 这是解决方案:

   @Injectable()
  export class StatisticsResolver implements Resolve<Statistic> {
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Statistic> {
        // return Observable.of(new Statistic());
        return this.store.select("statistic").map(statistic => {
        console.log("stats", statistic);

        return statistic;
    });
}

这是路线:

const routes: Routes = [
    {path: '', component: TelefonanlageComponent, resolve: {statistic:  TelefonieStatisticResolver}},
];

我不太明白为什么您要通过解析器将解析后的数据发送到您的组件。 设置 ngrx 存储的全部意义在于拥有单一的数据源。 因此,您在这里只想做的是确保数据是真实的。 休息,组件可以使用选择器从存储中获取数据。

我可以看到您正在从组件的ngOnInit方法中调用LOAD_ACTION 你不能调用一个动作来解析数据,这会导致你在Init上调用该动作的组件! 这就是你的路由器没有加载路由的原因。

不知道你是否理解,但这是有道理的!

简单来说,你正在做的就是这个。 您正在锁定房间,然后将钥匙从门下方的缝隙中推开,然后想知道为什么门没有打开。

随身携带钥匙!

守卫的解析方法应该调用LOAD_ACTION 然后解析应该等待数据加载,然后路由器应该继续组件。

你是怎样做的?

其他答案是设置订阅,但问题是您不想在您的警卫中这样做。 订阅守卫不是一个好习惯,因为他们需要被取消订阅,但是如果数据没有得到解析,或者守卫返回 false,那么路由器永远不会取消订阅,我们就会一团糟。

所以,使用take(n) 此运算符将从订阅中获取n值,然后自动终止订阅。

此外,在您的操作中,您将需要LOAD_STATISTICS_SUCCESSLOAD_STATISTICS_FAIL 因为您的服务方法可能会失败!

在 reducer State ,您需要一个loaded属性,当服务调用成功并调用LOAD_STATISTICS_SUCCESS操作时,该属性变为true

在主减速器中添加一个选择器, getStatisticsLoaded ,然后在您的 gaurd 中,您的设置将如下所示:

resolve(): Observable<boolean> {

this.store.dispatch({type: LOAD_STATISTICS});

return this.store.select(getStatisticsLoaded)
    .filter(loaded => loaded)
    .take(1);

}

因此,只有当加载的属性更改为 true 时,过滤器才会允许流继续。 take将取第一个值并传递。 此时解析完成,路由器可以继续。

同样, take将终止订阅。

我刚刚在 AngularFrance 提供宝贵意见后自己解决了这个问题。 由于我仍然是初学者,我不知道这是否应该这样做,但它确实有效。

我实现了这样的 CanActivate Guard:

@Injectable()
export class TelefonieStatisticGuard implements CanActivate {
    constructor(private store: Store<AppState>) {}

    waitForDataToLoad(): Observable<Statistic> {
        return this.store.select(state => state.statistic)
            .filter(statistic => statistic && !statistic.empty);
    }

    canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
        this.store.dispatch({type: LOAD_STATISTIC});

        return this.waitForDataToLoad()
            .switchMap(() => {
                return Observable.of(true);
            });
        }
    }
}

canActivate(...) 方法首先调度一个动作来加载数据。 在 waitForDataToLoad() 中,我们过滤数据已经存在并且不为空(我的业务逻辑的实现细节)。

在返回 true 之后,我们调用 switchMap 返回一个为 true 的 Observable。

我假设“路由器防护”是指resolve防护? 就像您希望激活路线之前加载数据一样?

如果是,那么一切都应该很好地协同工作:

  • 来自 ngrx/store 的值被暴露为 observables(例如在他们的文档中store.select('counter')包含一个 observable)。
  • 在 Angular resolve守卫中可以返回 observables

所以你可以简单地从你的解析中返回 ngrx/store observable:

class MyResolver implements Resolve<any> {

  constructor(private store: Store<any>) {}

  resolve(): Observable<any> {
    // Adapt code to your situation...
    return this.store.select('statistics');
  }
}

话虽如此,您的设置似乎确实有点复杂。 我倾向于将状态视为瞬态数据(菜单的折叠状态,在会话期间需要在多个屏幕/组件之间共享的数据,例如当前用户),但我不确定我会加载从后端到状态的大片数据。

您从将统计信息存储在状态中获得什么? (相对于简单地加载它们并在某个组件中显示它们)

如果加载不成功,那么您可能想取消导航。 可以通过抛出错误来完成。 最重要的是 - 返回的 observable 必须完成或以错误结束。

resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
): Observable<boolean> {

    this.store.dispatch(new LoadStatistic(route.params.someParam));

    return this.store.pipe(select(selectStatisticModel),
        filter(s => !s.isLoading),
        take(1),
        map(s => {
            if (!s.isLoadSuccess) {
                throw s.error;
            }
            return true;
        })
    );
}

这也考虑了失败的请求:

canActivate(): Observable<boolean> {
    this.store.dispatch({type: LOAD_STATISTICS);

    return this.store.select((state) => state.statistics)
      .filter((statistics) => statistics.loaded || statistics.loadingFailed)
      .map((statistics) => statistics.loaded);
  }

这个答案适用于在这种情况下使用 ngrx/effect 感到沮丧的人。 也许最好使用没有 ngrx/effects 的简单方法。

canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    // this will only dispatch actions to maybe clean up, NO EFFECTS 
    this.store.dispatch({type: LOAD_STATISTIK});     

  return this.service.loadOne()
     .do((data) => {
         this.store.dispatch({type: LoadSuccess, payload: data })
      }).map(() => {
      return true; 
    })  
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM