角材料对话框和ngrx

时间:2018-08-15 15:19:04

标签: angular typescript architecture angular-material ngrx

我正在使用Ngrx和Angular材料开发新的Angular 6应用程序。 我正在创建将由公司中的许多开发人员使用的基本应用程序。我的问题出在我要创建的对话框redux系统上。

我将首先分享我的实际代码,并说明问题和尝试的方法。

我的目标:在我的应用程序中的任何地方,我只想简单地调用一个操作即可打开自定义对话框(特定于每个功能)。该应用程序应打开多个全屏对话框。

这是我的简化架构:

AppModule
CoreModule
DialogsModule (StoreModule.forFeature('dialog', dialogReducer) / Effects.forFeature([DialogEffects]))
    FeatureAModule (contains specific dialogs component)
    FeatureBModule (contains specific dialogs component)

我想要的应用程序中的任何地方:

// Random Feature
 openDialog(): void {
    const payload: {
       componentOrTemplateRef: MyDialogComponent, // The dialog, create by dev, in a specific feature
       config: {
          id: 'my-custom-id',
          data: {
             ... // MAT_DIALOG_DATA
          }
       }
    };
    this.store.dispatch(new OpenDialogAction(payload));
}

我的实际对话框Redux:

dialog.action.ts

export enum DialogActionTypes {
  OPEN = '[DIALOG] OPEN',
  SAVE_REF = '[DIALOG] SAVE_REF' // use to store dialog reference in the ngrx store
  CLOSE = '[DIALOG] CLOSE'
}

export type DialogAction = OpenDialogAction | SaveRefDialogAction | CloseDialogAction;

export interface OpenDialogPayload {
  componentOrTemplateRef: ComponentType<any>;
  config: MatDialogConfig;
}

export interface CloseDialogPayload {
  dialogId: string;
  responseData?: any;
}

export class OpenDialogAction implements Action {
  readonly type = DialogActionTypes.OPEN;

  constructor(public payload: OpenDialogPayload) {}
}

export class SaveRefDialogAction implements Action {
  readonly type = DialogActionTypes.SAVE_REF;

  constructor(public payload: MatDialogRef<any>) {}
}

export class CloseDialogAction implements Action {
  readonly type = DialogActionTypes.CLOSE;

  constructor(public payload: CloseDialogPayload) {}
}

dialog.reducer.ts

export interface DialogState {
  refs: Array<{ id: string, ref: MatDialogRef<any> }>;
}

const initialState: DialogState = {
  refs: []
};

export function dialogReducer(state: DialogState = initialState, action: DialogAction): DialogState {
  switch (action.type) {
    case DialogActionTypes.SAVE_REF:
      return { ...state, refs: [...state.refs, { id: action.payload.id, ref: action.payload }] };
    case DialogActionTypes.CLOSE:
      return { ...state, refs: state.refs.filter(ref => ref.id !== action.payload.dialogId) };
    default:
      return state;
  }
}

// DialogState Selector
export const getDialogState = createFeatureSelector('dialog');

// DialogState property selectors
export const getDialogRefById = (id: string) => createSelector(getDialogState, (state: DialogState) => state.refs.find(ref => ref.id === id).ref);

dialog.effects.ts

@Injectable()
export class DialogEffects {
  @Effect()
  openDialog$: Observable<SaveRefDialogAction> = this.actions$.pipe(
    ofType(DialogActionTypes.OPEN),
    map((action: OpenDialogAction) => action.payload),
    switchMap((payload: OpenDialogPayload) => of(this.dialog.open(payload.componentOrTemplateRef, payload.config))),
    map((dialogRef: MatDialogRef<any>) => new SaveRefDialogAction(dialogRef))
  );

  @Effect({ dispatch: false })
  closeDialog$: Observable<{}> = this.actions$.pipe(
    ofType(DialogActionTypes.CLOSE),
    map((action: CloseDialogAction) => action.payload),
    tap((payload: CloseDialogPayload) => this.dialog.getDialogById(payload.dialogId).close(payload.responseData)),
    mapTo(of())
  );

  constructor(private actions$: Actions, private dialog: MatDialog) {}

我对功能的自定义对话框组件有疑问。它们未被DialogsModule识别(它们必须在entryComponents上)。因此,我创建了一个静态方法withComponents,该方法返回ModuleWithProviders并使用注入令牌ANALYZE_FOR_ENTRY_COMPONENTS

填充entryComponents。
@NgModule({
  imports: [
    MatDialogModule,
    StoreModule.forFeature('dialog', dialogReducer),
    EffectsModule.forFeature([DialogEffects])
  ]
})
export class DialogsModule {
  static withComponents(components: any) ModuleWithProviders {
    return {
        ngModule: DialogsModule,
        providers: [{ provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: components, multi: true }]
    };
  }
}

问题

我所有带有自定义对话框的功能都需要导入DialogsModule ...但是,每次都会对DialogEffects进行实例化(如果我有3个必须导入DialogsModule的模块,那么DialogEffects将被实例化3次)。

如何在没有这个问题和entryComponents问题的情况下拥有正确的物料对话框管理器?我愿意接受任何建议。

先谢谢您!

1 个答案:

答案 0 :(得分:0)

您可以为模块使用forRoot和forFeature。 Link here

对于根模块,将服务添加为单例(如效果)。对于功能模块,您可以添加其他模块。

您可以将单例服务与providedIn: 'root'一起使用,但实际上我不知道它是否可以与NgRx效果一起使用。

P.S。另一方面,如果您使用HMR恢复状态,则模态将不会保持打开状态。