如何动态修改Effect允许类型实现通知模块的动态触发?
我来帮你一步步搞定这个动态通知模块,基于NgRx的话,核心思路是通过状态管理维护可触发通知的action列表,然后让Effect动态监听这个列表的变化,自动订阅对应的动作来显示Snackbar。下面是具体的实现步骤:
1. 定义通知模块的核心类型与Actions
首先我们要明确触发器的结构,以及用于添加触发器、显示Snackbar的动作:
// notification.actions.ts import { createAction, props } from '@ngrx/store'; // 定义触发器类型:关联action类型和对应的通知模板 export interface NotificationTrigger { actionType: string; // 比如 '[Product] Item Added' messageTemplate: string; // 比如 '[Product] item added' } // 外部模块调用的添加触发器动作 export const addTrigger = createAction( '[Notification] Add Trigger', props<{ trigger: NotificationTrigger }>() ); // 内部用于触发Snackbar显示的动作 export const showSnackbar = createAction( '[Notification] Show Snackbar', props<{ message: string }>() );
2. 配置通知状态与Reducer
创建状态来存储所有已添加的触发器,并编写Reducer处理addTrigger动作:
// notification.reducer.ts import { createReducer, on } from '@ngrx/store'; import { addTrigger, NotificationTrigger } from './notification.actions'; export interface NotificationState { triggers: NotificationTrigger[]; } export const initialState: NotificationState = { triggers: [] }; export const notificationReducer = createReducer( initialState, on(addTrigger, (state, { trigger }) => ({ ...state, triggers: [...state.triggers, trigger] })) );
同时创建对应的Selector,方便后续Effect获取触发器列表:
// notification.selectors.ts import { createFeatureSelector, createSelector } from '@ngrx/store'; import { NotificationState } from './notification.reducer'; export const selectNotificationState = createFeatureSelector<NotificationState>('notification'); export const selectTriggers = createSelector( selectNotificationState, (state) => state.triggers );
3. 实现动态订阅的核心Effect
这是实现动态监听的关键:我们通过switchMap监听触发器状态的变化,每次更新时重新构建一个监听对应action类型的流:
// notification.effects.ts import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { map, switchMap, withLatestFrom } from 'rxjs/operators'; import { NotificationState } from './notification.reducer'; import { addTrigger, showSnackbar } from './notification.actions'; import { selectTriggers } from './notification.selectors'; import { of } from 'rxjs'; import { MatSnackBar } from '@angular/material/snack-bar'; // 假设用Angular Material的Snackbar @Injectable() export class NotificationEffects { // 动态监听触发器对应的action,触发Snackbar显示 showNotification$ = createEffect(() => this.store.select(selectTriggers).pipe( switchMap(triggers => { // 提取所有需要监听的action类型 const allowedActionTypes = triggers.map(trigger => trigger.actionType); // 如果没有触发器,返回空流避免无效监听 if (allowedActionTypes.length === 0) return of(); // 订阅所有匹配的action类型 return this.actions$.pipe( ofType(...allowedActionTypes), withLatestFrom(this.store.select(selectTriggers)), map(([action, triggers]) => { // 找到当前action对应的触发器模板 const matchedTrigger = triggers.find(t => t.actionType === action.type); if (!matchedTrigger) return; // 这里可以根据action的payload动态修改消息(比如替换模板变量) // 示例:如果模板是 '[Product] {name} added',可以用action.payload.name替换 const finalMessage = matchedTrigger.messageTemplate; return showSnackbar({ message: finalMessage }); }) ); }) ) ); // 处理显示Snackbar的动作 displaySnackbar$ = createEffect(() => this.actions$.pipe( ofType(showSnackbar), tap(({ message }) => { this.snackBar.open(message, '关闭', { duration: 3000, horizontalPosition: 'end', verticalPosition: 'bottom' }); }) ), { dispatch: false } // 无需分发新动作 ); constructor( private actions$: Actions, private store: Store<{ notification: NotificationState }>, private snackBar: MatSnackBar ) {} }
4. 其他模块如何添加触发器
比如在Product模块中,你可以在模块初始化时添加对应的触发器:
// product.effects.ts import { Injectable } from '@angular/core'; import { OnInitEffects } from '@ngrx/effects'; import { addTrigger } from '../notification/notification.actions'; import { of } from 'rxjs'; @Injectable() export class ProductEffects implements OnInitEffects { ngrxOnInitEffects() { // 添加Product模块的通知触发器 return of( addTrigger({ trigger: { actionType: '[Product] Item Added', messageTemplate: '[Product] item added' } }), addTrigger({ trigger: { actionType: '[Product] Item Removed', messageTemplate: '[Product] item removed' } }) ); } }
或者在组件初始化时添加:
// product-list.component.ts import { Store } from '@ngrx/store'; import { addTrigger } from '../notification/notification.actions'; constructor(private store: Store) {} ngOnInit() { this.store.dispatch(addTrigger({ trigger: { actionType: '[Product] Item Added', messageTemplate: '[Product] item added' } })); }
5. 模块配置
最后别忘了在根模块中配置NgRx的Store和Effects:
// app.module.ts import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import { notificationReducer } from './notification/notification.reducer'; import { NotificationEffects } from './notification/notification.effects'; import { ProductEffects } from './product/product.effects'; import { MatSnackBarModule } from '@angular/material/snack-bar'; @NgModule({ imports: [ // ... 其他模块 MatSnackBarModule, StoreModule.forRoot({ notification: notificationReducer // ... 其他reducers }), EffectsModule.forRoot([NotificationEffects, ProductEffects]) ] }) export class AppModule {}
这样一来,每当其他模块通过addTrigger添加新的action类型后,通知模块的Effect会自动更新监听列表,对应的action触发时就会显示对应的Snackbar了。
内容的提问来源于stack exchange,提问作者Amsakanna
相关产品推荐
相关产品推荐

