Problem abstract
- Make the mutation or action of Vuex store module A call the external function. It could be the mutation or action of other Vuex store module (e. g. B).
- A must save link to external method (e. g. mutation or action of other Vuex store module B) because it will be invoked indirectly and not immediately.
In below example:
- A will be
RollbackActionFloatingPanelStoreModule - B will be
OtherStoreModule - The external function will be
rollbackRemoveItem
Example (when it could be used)
This floating undo panel allows, obviously, undo the user's actions, however the undo method must be implemented. Also, because the undo panel must rollback multiple kinds of user's control actions, each time we need to specify the undo method implementation.
$ref is not good for TypeScript-based Vue, so let $ref solution will be the last hope. Here is I want fully delegate the undo panel state and behavior to Vuex module, so the Vue component will be logicless:
import { Vue, Component } from "vue-property-decorator";
import { getModule } from "vuex-module-decorators";
import template from "./RollbackActionFloatingPanel.pug";
import RollbackActionFloatingPanelStoreModule
from "@Store/modules/AssociatedWithComponents/RollbackActionFloatingPanel";
@Component({ template })
export default class RollbackActionFloatingPanel extends Vue {
private readonly relatedStoreModule: RollbackActionFloatingPanelStoreModule =
getModule(RollbackActionFloatingPanelStoreModule);
}
Component template (pug language; svg icons are encapsuled to pug mixins)
transition(name="rollback_action_floating_panel")
.RollbackActionFloatingPanel(v-show="relatedStoreModule.displayFlag")
+Checkmark__Simple__Bold--MaterialDesignIcon.RollbackActionFloatingPanel-CheckmarkIcon
.RollbackActionFloatingPanel-Text {{ relatedStoreModule.message }}
button.RollbackActionFloatingPanel-Button(@click="relatedStoreModule.onClickRollbackButton")
+Undo__Simple--MaterialDesignIcon.ButtonWithPrependedIcon-Icon
| Undo
button.RollbackActionFloatingPanel-Button.RollbackActionFloatingPanel-Button__Primary(@click="relatedStoreModule.dismiss")
+Hide__StrikethroughEye__Filled--MaterialDesignIcon.ButtonWithPrependedIcon-Icon
| Hide
The vuex store RollbackActionFloatingPanel needs only one public method: displayAndHideALittleLater. It could be called from other store module or Vue component. Logically, we could specify undo method implementation as parameter (displayAndHideALittleLater(payload: { message: string; onClickRollbackButton: Function; })).
Methods onClickRollbackButton and dismiss are being called from template, so TypeScript can not forbid it's invocation in this case even those are not public.
import { VuexModule, Module, Mutation, Action } from "vuex-module-decorators";
import store, { StoreModuleNames } from "@Store/Store";
@Module({
name: "VUEX_MODULE:UNDO_PANEL",
store,
dynamic: true,
namespaced: true
})
export default class RollbackActionFloatingPanelStoreModule extends VuexModule {
private static readonly AUTO_HIDE_TIMEOUT__MILLISECONDS: number = 5000;
private _displayFlag: boolean = false;
private _message: string = "";
private _onClickRollbackButton: Function = () => {};
@Action
public displayAndHideALittleLater(
{
message,
onClickRollbackButton
}: {
message: string;
onClickRollbackButton: Function;
}
): void {
this.setMessage(message);
this.setOnClickRollbackButtonEventHandler(onClickRollbackButton);
this.display();
setTimeout(
(): void => { this.dismiss(); },
RollbackActionFloatingPanelStoreModule.AUTO_HIDE_TIMEOUT__MILLISECONDS
);
}
@Action
private onClickRollbackButton(): void {
this._onClickRollbackButton();
}
@Mutation
private setMessage(message: string): void {
this._message = message;
}
@Mutation
private setOnClickRollbackButtonEventHandler(newHandler: Function): void {
this._onClickRollbackButton = newHandler;
}
@Mutation
private display(): void {
this._displayFlag = true;
}
@Mutation
private dismiss(): void {
this._displayFlag = false;
this._onClickRollbackButton = () => {};
}
public get displayFlag(): boolean { return this._displayFlag; }
public get message(): string { return this._message; }
}
The usage from other Vuex module:
import { VuexModule, Module, Mutation, getModule } from "vuex-module-decorators";
import store, { StoreModuleNames } from "@ProjectInitializer:Store/Store";
import RollbackActionFloatingPanelStoreModule
from "@ProjectInitializer:Store/modules/AssociatedWithComponents/RollbackActionFloatingPanel";
@Module({
name: "VUEX_MODULE:OTHER"
store,
dynamic: true,
namespaced: true
})
export default class OtherStoreModule extends VuexModule {
private _selectedItems: Array<Item> = getDefaultItemsFromRepsitory();
private _lastDeletedItem: Item | null = null;
@Mutation
public removeItem(targetItemId: string): void {
const targetItemSearchingPredicate: (item: Item) => boolean =
(item: Item): boolean => item.id === targetItemId;
const targetItem: Item | undefined = this._selectedItems.find(targetItemSearchingPredicate);
if (typeof targetItem === "undefined") { return; }
this._lastDeletedItem = targetEntryPointsGroupSettings;
const indexOfTargetItem: number = this._selectedItems.findIndex(targetItemSearchingPredicate);
if (indexOfTargetItem !== -1) {
this._selectedItems.splice(indexOfTargetItem, 1);
} else {
return;
}
getModule(RollbackActionFloatingPanelStoreModule).displayAndHideALittleLater({
message: "Item deleted",
onClickRollbackButton: (): void => { this.rollbackRemoveItem(); }
});
}
@Mutation
public rollbackRemoveItem(): void {
if (this._lastDeletedItem === null) { return; }
this._selectedItems.unshift(this._lastDeletedItem);
this._lastDeletedItem = null;
}
}
error
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 onClickRollbackButton
occurs. Because all my modules are dynamics, maybe error message is not accurate. In fact, we call mutation rollbackRemoveItem via non-decorated function _onClickRollbackButton via action onClickRollbackButton. I can not avoid _onClickRollbackButton so easily, because I need to store undo method implementation somewhere until dismiss mutation will be executed.
Attempt with static class field
@Module({
name: "VUEX_MODULE:UNDO_PANEL",
store,
dynamic: true,
namespaced: true
})
export default class RollbackActionFloatingPanelStoreModule extends VuexModule {
private static readonly AUTO_HIDE_TIMEOUT__MILLISECONDS: number = 5000;
private _displayFlag: boolean = false;
private _message: string = "";
private static _onClickRollbackButton: Function = () => {};
// ...
@Action
private onClickRollbackButton(): void {
RollbackActionFloatingPanelStoreModule._onClickRollbackButton();
}
@Mutation
private setOnClickRollbackButtonEventHandler(newHandler: Function): void {
RollbackActionFloatingPanelStoreModule._onClickRollbackButton = newHandler;
}
}
Same error.
Repro
I'll share the repro as GitHub Repository until this problem will be solved. If you will be kind enough to clone or download this repository, please execute npm i && npm run JavaScriptBuild in the project directory. Once all npm dependencies will be installed, webpack will build the project and you can open the result on http://localhost:8081/

