You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何动态修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:42:48