Assume that we need the single modal dialog with dynamic content. To access to this modal's API from any component, we can use below Vuex store. Below solution has been checked in real project and working fine:
import store from "@Store/store";
import { Action, Mutation, VuexModule, Module, getModule } from "vuex-module-decorators";
type UnifiedModalDialogOptions = {
targetContentID: string;
mainTitle?: string;
};
@Module({
name: "VUEX_MODULE/COMPONENTS/UNIFIED_MODAL",
store,
dynamic: true,
namespaced: true
})
export default class UnifiedModalDialogService extends VuexModule {
private _displayFlag: boolean = false;
private _activeContent: string | null = null;
private _mainTitle: string | null = null;
@Action
public displayDialog({
targetContentID,
mainTitle
}: UnifiedModalDialogOptions): void {
this.setActiveContentID(targetContentID);
this.setSizingMode(sizingMode);
if (!isUndefined(mainTitle)) {
this.setMainTitle(mainTitle);
}
if (!isUndefined(sizingCustomCSS_Class)) {
this.setCustomSizingCSS_Class(sizingCustomCSS_Class);
}
this.setDisplayFlag(true);
}
@Mutation
public dismiss(): void {
this._displayFlag = false;
this._activeContent = null;
this._mainTitle = null;
}
public get displayFlag(): boolean { return this._displayFlag; }
public get mainTitle(): string | null { return this._mainTitle; }
public get activeContentID(): string | null { return this._activeContent; }
@Mutation
private setDisplayFlag(newFlagValue: boolean): void {
this._displayFlag = newFlagValue;
}
@Mutation
private setActiveContentID(targetContentID: string): void {
this._activeContent = targetContentID;
}
@Mutation
private setMainTitle(newMainTitle: string): void {
this._mainTitle = newMainTitle;
}
}
Now what if we need to call some additional function when closing modal?
For instance, in below example, when we click "close" button of modal or click the dim underlay under modal, we need to utilize the account editing by finishAccountEditing
method:
import { Vue, Component } from "vue-property-decorator";
import { getModule } from "vuex-module-decorators";
import UnifiedModalDialogService
from "@Components:SharedSingletons/UnifiedModalDialog/UnifiedModalDialogService.vuex";
@Component
export default class AccountsManagementPage extends Vue {
private onClickEditAccountButton(targetAccount: Account): void {
private inputtedAccountName: string = "";
private inputtedEmail: string = "";
this.inputtedAccountName = targetAccount.name;
this.inputtedEmail = targetAccount.email;
getModule(UnifiedModalDialogService).displayDialog({
targetContentID: this.ACCOUNT_ADDING_AND_EDITING_MODAL_DIALOG_ID,
mainTitle: StaticStrings.modalDialogsTitles.accountEditing,
sizingMode: UnifiedModalDialogService.SizingModes.fixedMaximalWidth,
});
}
private finishAccountEditing() void {
this.inputtedAccountName = "";
this.inputtedEmail = "";
}
}
Please note that the modal dialog component is not the child component, so we cant's access to it by reference.
Attempts and experiments
The logically right solution is to pass the function to vuex module and remember it:
@Component
export default class AccountsManagementPage extends Vue {
private onClickEditAccountButton(targetAccount: Account): void {
private inputtedAccountName: string = "";
private inputtedEmail: string = "";
this.inputtedAccountName = targetAccount.name;
this.inputtedEmail = targetAccount.email;
getModule(UnifiedModalDialogService).displayDialog({
targetContentID: this.ACCOUNT_ADDING_AND_EDITING_MODAL_DIALOG_ID,
mainTitle: StaticStrings.modalDialogsTitles.accountEditing,
sizingMode: UnifiedModalDialogService.SizingModes.fixedMaximalWidth,
// added ↓
onClose: this.finishAccountEditing.bind(this)
});
}
private finishAccountEditing() void {
this.inputtedAccountName = "";
this.inputtedEmail = "";
}
}
Now I'll try to call it inside UnifiedModalDialogService
module (just for experiment - inside displayDialog
):
@Action
public displayDialog({
targetContentID,
sizingMode,
sizingCustomCSS_Class,
mainTitle,
onClose
}: UnifiedModalDialogOptions): void {
onClose();
// ...
}
The result is unexpected:
index.js?0da5:363 Uncaught (in promise) Error: ERR_ACTION_ACCESS_UNDEFINED:
Are you trying to access this.someMutation() or this.someGetter inside an @Action?
That works only in dynamic modules.
If not dynamic use this.context.commit("mutationName", payload) and this.context.getters["getterName"]
Error: Could not perform action displayDialog
At least two strange moments:
Are you trying to access this.someMutation() or this.someGetter inside an @Action?
No.
That works only in dynamic modules.
My module is dynamic:
@Module({
name: "VUEX_MODULE/COMPONENTS/UNIFIED_MODAL",
store,
dynamic: true,
namespaced: true
})
export default class UnifiedModalDialogService extends VuexModule {
//...
}
The next experiment is delegating onClose
calling to static method.
export default class UnifiedModalDialogService extends VuexModule {
@Action
public displayDialog({
targetContentID,
sizingMode,
sizingCustomCSS_Class,
mainTitle,
onClose
}: UnifiedModalDialogOptions): void {
UnifiedModalDialogService._onClose(onClose);
this.setActiveContentID(targetContentID);
this.setSizingMode(sizingMode);
if (!isUndefined(mainTitle)) {
this.setMainTitle(mainTitle);
}
if (!isUndefined(sizingCustomCSS_Class)) {
this.setCustomSizingCSS_Class(sizingCustomCSS_Class);
}
this.setDisplayFlag(true);
}
private static _onClose(implementation: () => unknown): void {
implementation();
}
}
The result is identical:
index.js?0da5:363 Uncaught (in promise) Error: ERR_ACTION_ACCESS_UNDEFINED
Herewith, generally the calling of static methods is possible:
@Module({
name: "VUEX_MODULE/COMPONENTS/UNIFIED_MODAL",
store,
dynamic: true,
namespaced: true
})
export default class UnifiedModalDialogService extends VuexModule {
@Action
public displayDialog({
targetContentID,
sizingMode,
sizingCustomCSS_Class,
mainTitle,
onClose
}: UnifiedModalDialogOptions): void {
UnifiedModalDialogService.staticMethodExample();
// ...
}
private static staticMethodExample(): void {
console.log("Im am vuex module static method!");
}
}