如何使用角度路由器防护装置附加ngrx / store

时间:2017-02-28 11:53:41

标签: angular angular2-routing ngrx ngrx-effects

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

我已经使用ngrx / store成功设置了我的角度项目,并且我可以在初始化主要组件后调度加载操作,如下所示:

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

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

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

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

reduce(oldstate: Statistics = initialStatistics, action: Action): Statistik {
    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<Statistik> {
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Statistik> {
        // return Observable.of(new Statistik());
        return this.store.select("statistik").map(statistik => {
        console.log("stats", statistik);

        return statistik;
    });
}

这是路线:

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

6 个答案:

答案 0 :(得分:7)

我不太明白为什么要通过解析器将已解析的数据发送到您的组件。设置ngrx存储的关键是拥有单一的数据源。所以,你想在这里做的是确保数据是真的。休息时,组件可以使用选择器从商店获取数据。

我可以看到您从组件的LOAD_ACTION方法调用ngOnInit。您无法调用操作来解析数据,然后导致该组件在Init上调用该操作!这就是您的路由器没有加载路由的原因。

不确定你是否明白这一点,但这是有意义的!

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

随身携带钥匙!

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

你是怎么做到的?

其他答案是设置订阅,但问题是你不想在你的警卫中这样做。在警卫中订阅是不好的做法,因为他们需要取消订阅,但如果数据没有得到解决,或者警卫返回错误,那么路由器永远不会取消订阅,我们有一个混乱。

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

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

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

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

resolve(): Observable<boolean> {

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

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

}

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

同样,take将终止订阅。

答案 1 :(得分:6)

我在AngularFrance的宝贵意见后自己解决了这个问题。由于我还是初学者,我不知道这是不是应该如何完成,但它确实有效。

我实现了一个CanActivate Guard:

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

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

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

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

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

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

答案 2 :(得分:4)

我假设是&#34;路由器后卫&#34;你的意思是resolve后卫?在激活路线之前,您是否希望在中加载数据?

如果是,那么一切都应该很好地结合在一起:

  • 来自ngrx / store的值作为可观察对象公开(例如,在他们的文档中store.select('counter')包含一个可观察对象)。
  • 在Angular resolve警卫can return 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');
  }
}

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

您在状态中存储统计信息会获得什么?(与仅加载它们并在某些组件中显示它们相比)

答案 3 :(得分:2)

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

collection_name

答案 4 :(得分:0)

这也考虑了失败的请求:

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);
  }

答案 5 :(得分:0)

对于在这种情况下使用ngrx / effect感到沮丧的人的答案。也许更好地使用没有ngrx /效果的简单方法。

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; 
    })  
}