我目前正在使用NgRx和Router Store构建一个应用程序。在一个组件中有一个下拉菜单,它改变了url中的参数,并在同一个组件上导航。因为在同一个组件上导航不会调用NgOnInit,所以不会调用相应的效果(调用API的效果)。在我看来,如果组件改变了参数,则应该始终调用该效果。
loadTransactions$ = createEffect(() => this.actions$.pipe(
ofType(
NavigationActions.ActionType.NavigateToTransactionsSuccess,
TransactionsPageActions.ActionType.LoadPage
),
[...]
));
我有一个想法,是创建一个自定义操作,只监听组件上的导航与下拉菜单,但我不知道如何做到这一点。
我当前的解决方案如下:
this.store.select(RouterSelectors.selectUrl).subscribe(() =>
this.store.dispatch(TransactionsPageActions.loadPage())
);
但这感觉可能会在未来导致一些bug。有什么办法可以干净利落地做到这一点吗?
既然你想通过路由控制数据的加载,那么通过routeguard(特别是CanActivate
Guard)来执行transactionData的提取将是理想的和更干净的。
下面是一个可能的方法的简单片段,
routing.module.ts
const routes: Routes = [
{
path: '',
component: ShellPageComponent,
canActivate: [],
children: [
{
path: '',
component: MainParentComponent,
children: [
{
path: ':transactionId',
canActivate: [TransactionGuard],
component:TransactionComponent
},
{ path: '**', redirectTo: 'MainParentComponent' }
]
},
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class RoutingModule {}
transaction-page.guard.ts
@Injectable({
providedIn: 'root'
})
export class TransactionGuard implements CanActivate {
constructor( private store: Store<any> ) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean | UrlTree> {
const transcationId = route.paramMap.get('transactionId')
//assuming TransactionIsLoadedSelector is a selector to verify if transaction has been loaded and returns a Boolean
return this.store.select(TransactionIsLoadedSelector$,{id:transcationId}).pipe(
tap((TransactionIsLoaded)=>{
if(!TransactionIsLoaded){
this.store.dispatch(TransactionsPageActions.loadPage())
}
}),
filter((TransactionIsLoaded)=> TransactionIsLoaded)
)
}
}
我更仔细地阅读了文档,效果指南告诉你以下内容:
注意:事件流并不局限于分派的动作,它可以是任何产生新动作的可观察对象,比如Angular Router中的可观察对象、从浏览器事件中创建的可观察对象,以及其他可观察流。
所以我实现了一个自定义的路由器动作,并在一个效果中调度它,当路由器存储中的accountId发生变化时调用它。
accountIdChanged$ = createEffect(() => this.store.select(
RouterSelectors.selectRouteParam('accountId')
).pipe(
map((accountId) => RouterActions.accountIdChanged({accountId}))
));
现在我可以在任何我想要的地方听到这个动作,例如在我的事务效果。
loadTransactions$ = createEffect(() => this.actions$.pipe(
ofType(RouterActions.ActionType.AccountIdChanged),
// [...]
));