Search code examples
angularmodal-dialogstatengxs

Opening modal using NGX and state management


I'd like to use NGXS to open modal which will set column visibility for datatable.

Here is my code:

state.ts file:

@Action(OpenColumnModal)
openColumnModal(ctx: StateContext<FeedStateModel>) {
    const state = ctx.getState();
    const allCols = state.allColumns;
    return this.modalService.openColumnVisibilityModal(allCols).pipe(tap((result) => {
        ctx.setState({
            ...state,
            allColumns: result,
            userColumns: result.filter(col => col.visible)
        });
    })
}

modal.service.ts:

openColumnVisibilityModal(columns): Observable<any> {
    const dialogRef = this.dialog.open(ColumnVisibilityModal, {
        data: columns,
        autoFocus: false,
        hasBackdrop: true,
        disableClose: true
    });

    return dialogRef.afterClosed();
}

When I'm using modal opened by NGXS, after closing the state event isn't emitted. After that, I need to click somewhere to call the callback function inside openColumnModal function.

I'm using Angular Material dialog.

Does anyone know how to call callback function automatically after closing modal?

Thanks in advance:)


Solution

  • P.S. - other answers that advise to subscribe inside the action handler are not correct, as NGXS doesn't work like that!

    Your current approach is correct, the problem is that action handlers are run outside Angular's zone. Just inject the NgZone class into your state and execute the code within Angular's zone:

    constructor(private modalService: ModalService, private zone: NgZone) {}
    
    @Action(OpenColumnModal)
    openColumnModal(ctx: StateContext<FeedStateModel>) {
      const state = ctx.getState();
      const allCols = state.allColumns;
      return this.zone.run(() =>
        this.modalService.openColumnVisibilityModal(allCols).pipe(
          tap(result => {
            ctx.setState({
              ...state,
              allColumns: result,
              userColumns: result.filter(col => col.visible)
            });
          })
        )
      );
    }
    

    When you dispatch any action - NGXS invokes the appropriate handlers for this action within the parent zone using runOutsideAngular, this is by design.

    You can also look at the executionStrategy option that allows to provide own class or use existing NoopNgxsExecutionStrategy, that doesn't use NgZone class at all.