我是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}},
];
答案 0 :(得分:7)
我不太明白为什么要通过解析器将已解析的数据发送到您的组件。设置ngrx存储的关键是拥有单一的数据源。所以,你想在这里做的是确保数据是真的。休息时,组件可以使用选择器从商店获取数据。
我可以看到您从组件的LOAD_ACTION
方法调用ngOnInit
。您无法调用操作来解析数据,然后导致该组件在Init
上调用该操作!这就是您的路由器没有加载路由的原因。
不确定你是否明白这一点,但这是有意义的!
简单来说,你在做什么就是这个。您正在锁定房间,然后将钥匙推过门下方的缝隙,然后想知道为什么门没有打开。
随身携带钥匙!
守卫的解决方法应该调用LOAD_ACTION
。然后解决方案应该等待加载数据,然后路由器应该继续到组件。
你是怎么做到的?
其他答案是设置订阅,但问题是你不想在你的警卫中这样做。在警卫中订阅是不好的做法,因为他们需要取消订阅,但如果数据没有得到解决,或者警卫返回错误,那么路由器永远不会取消订阅,我们有一个混乱。
所以,请使用take(n)
。此运算符将从订阅中获取n
个值,然后自动终止订阅。
此外,在您的操作中,您需要LOAD_STATISTICS_SUCCESS
和LOAD_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
后卫?在激活路线之前,您是否希望在中加载数据?
如果是,那么一切都应该很好地结合在一起:
store.select('counter')
包含一个可观察对象)。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;
})
}