Angular应用中基于NgRx实现路由变化时的通知分发
实现NgRx Angular应用路由变化时的通知分发
看起来你已经把NgRx的核心模块(actions、reducers、effects)配置到位了,复用组件的路由设计也很合理。要实现路由变化时自动分发对应的NgRx action,我们可以借助@ngrx/router-store来监听路由事件,然后根据路由参数触发不同的业务action。下面是具体的实现步骤:
1. 定义路由相关的NgRx Actions
首先,我们需要创建对应路由场景的action,比如初始化新条目表单、加载现有条目用于编辑:
import { createAction, props } from '@ngrx/store'; import { Item } from './item.model'; // 初始化新条目表单状态 export const initializeNewItem = createAction('[Item Form] Initialize New Item'); // 请求加载指定ID的条目 export const loadItem = createAction('[Item Form] Load Item', props<{ itemId: string }>()); // 加载条目成功后的action(如果需要从API获取数据) export const loadItemSuccess = createAction('[Item API] Load Item Success', props<{ item: Item }>());
2. 编写Effect监听路由导航事件
接下来,我们在Effect中监听ROUTER_NAVIGATION事件(来自@ngrx/router-store),过滤出/item/开头的路由,解析参数后分发对应的action:
import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { RouterNavigationAction, ROUTER_NAVIGATION } from '@ngrx/router-store'; import { filter, map, switchMap } from 'rxjs/operators'; import { initializeNewItem, loadItem, loadItemSuccess } from './item.actions'; import { ItemService } from './item.service'; @Injectable() export class ItemEffects { constructor( private actions$: Actions, private itemService: ItemService ) {} // 监听路由跳转到item页面的事件 handleItemRouteNavigation$ = createEffect(() => this.actions$.pipe( ofType(ROUTER_NAVIGATION), // 过滤出/item/开头的路由 filter((action: RouterNavigationAction) => action.payload.routerState.url.startsWith('/item/') ), map((action: RouterNavigationAction) => { const urlSegments = action.payload.routerState.url.split('/'); const itemId = urlSegments[2]; if (itemId === 'new') { // 跳转到新增页面,分发初始化表单的action return initializeNewItem(); } else { // 跳转到编辑页面,分发加载条目的action return loadItem({ itemId }); } }) ) ); // 处理加载条目的API请求逻辑 loadItem$ = createEffect(() => this.actions$.pipe( ofType(loadItem), switchMap((action) => this.itemService.getItem(action.itemId).pipe( map((item) => loadItemSuccess({ item })) ) ) ) ); }
3. 更新Reducer维护表单状态
在Reducer中处理上述action,更新store里的表单状态(比如当前条目数据、是否处于编辑模式):
import { createReducer, on } from '@ngrx/store'; import { Item } from './item.model'; import { initializeNewItem, loadItemSuccess } from './item.actions'; export interface ItemFormState { currentItem: Item | null; isEditing: boolean; } export const initialState: ItemFormState = { currentItem: null, isEditing: false, }; export const itemFormReducer = createReducer( initialState, on(initializeNewItem, (state) => ({ ...state, currentItem: null, isEditing: false, })), on(loadItemSuccess, (state, { item }) => ({ ...state, currentItem: item, isEditing: true, })) );
4. 在复用组件中订阅Store状态
最后,在你的复用表单组件中,订阅store的状态来自动初始化表单:
import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { Item } from './item.model'; import { ItemFormState } from './item.reducer'; // 记得创建对应的selectors来获取store中的状态 import { selectCurrentItem, selectIsEditing } from './item.selectors'; @Component({ selector: 'app-item-form', templateUrl: './item-form.component.html', styleUrls: ['./item-form.component.css'] }) export class ItemFormComponent implements OnInit { itemForm: FormGroup; currentItem$: Observable<Item | null>; isEditing$: Observable<boolean>; constructor( private fb: FormBuilder, private store: Store<{ itemForm: ItemFormState }> ) { // 初始化表单控件 this.itemForm = this.fb.group({ name: [''], description: [''] // 其他表单字段... }); // 订阅store中的状态 this.currentItem$ = this.store.select(selectCurrentItem); this.isEditing$ = this.store.select(selectIsEditing); } ngOnInit(): void { // 当当前条目变化时,自动填充表单 this.currentItem$.subscribe(item => { if (item) { this.itemForm.patchValue(item); } else { this.itemForm.reset(); } }); } // 提交表单时,根据编辑状态分发对应action onSubmit(): void { if (this.itemForm.valid) { this.isEditing$.subscribe(isEditing => { if (isEditing) { // 分发更新条目的action(你已有的updateItem action) this.store.dispatch(updateItem({ item: { ...this.currentItem$, ...this.itemForm.value } })); } else { // 分发新增条目的action(你已有的createItem action) this.store.dispatch(createItem({ item: this.itemForm.value })); } }); } } }
5. 启用Router Store连接
别忘了在你的根模块中导入StoreRouterConnectingModule,让NgRx能够监听路由变化:
import { NgModule } from '@angular/core'; import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import { StoreRouterConnectingModule } from '@ngrx/router-store'; import { itemFormReducer } from './item.reducer'; import { ItemEffects } from './item.effects'; @NgModule({ imports: [ StoreModule.forRoot({ itemForm: itemFormReducer }), EffectsModule.forRoot([ItemEffects]), // 启用路由与NgRx Store的连接 StoreRouterConnectingModule.forRoot() ] }) export class AppModule {}
这样一来,当用户跳转到/item/new时,会自动分发initializeNewItem action重置表单;跳转到/item/:id时,会分发loadItem action加载对应条目数据,完美适配你复用组件的路由设计。
内容的提问来源于stack exchange,提问作者pjlamb12
相关产品推荐
相关产品推荐

