Динамическая функция для модуля магазина Vuex на примере плавающей панели отмены - PullRequest
2 голосов
/ 23 октября 2019

Краткое описание проблемы

  1. Сделать мутацию или действие модуля хранилища Vuex A вызовом внешней функции. Это может быть мутация или действие другого модуля хранилища Vuex (например, B ).
  2. A должна сохранить ссылку на внешний метод (например, мутация или действие другого Vuex)хранить модуль B ), поскольку он будет косвенным, а не немедленным.

В приведенном ниже примере:

  • A будет RollbackActionFloatingPanelStoreModule
  • B будет OtherStoreModule
  • Внешняя функция будет rollbackRemoveItem

Пример (когда это можно использовать)

Эта плавающая панель отмен позволяет, очевидно, отменять действия пользователя, однако метод отмены должен быть реализован. Кроме того, поскольку панель отмены должна выполнять откат нескольких видов управляющих действий пользователя, каждый раз, когда нам нужно указать реализацию метода отмены.

enter image description here

$refне подходит для Vue на основе TypeScript, так что пусть решение $ref будет последней надеждой. Вот я хочу полностью делегировать состояние и поведение панели отмены модулю Vuex, поэтому компонент Vue будет без логики:

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);
}

Шаблон компонента (язык мопса; значки SVG заключены в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

Для хранилища vuex RollbackActionFloatingPanel нужен только один публичный метод: displayAndHideALittleLater. Он может быть вызван из другого модуля магазина или компонента Vue. Логически, мы можем указать реализацию метода отмены как параметр (displayAndHideALittleLater(payload: { message: string; onClickRollbackButton: Function; })).

Методы onClickRollbackButton и dismiss вызываются из шаблона, поэтому TypeScript не может запретить его вызов в этом случае, даже если это не так. 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; }
}

Использование из другого модуля Vuex:

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;
  }
}

ошибка

enter image description here

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

происходит. Поскольку все мои модули являются динамическими, возможно, сообщение об ошибке не является точным. Фактически, мы вызываем мутацию rollbackRemoveItem через неукрашенную функцию _onClickRollbackButton через действие onClickRollbackButton. Я не могу избежать _onClickRollbackButton так легко, потому что мне нужно где-то хранить реализацию метода отмены до тех пор, пока не будет выполнена мутация dismiss.

Попытка с полем статического класса

@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;
  }
}

Та же ошибка.


Repro

Я поделюсь с ним как GitHub Repository , пока эта проблема не будет решена. Если вы будете достаточно любезны, чтобы клонировать или загрузить этот репозиторий, выполните npm i && npm run JavaScriptBuild в каталоге проекта. Как только все зависимости npm будут установлены, webpack соберет проект, и вы можете открыть результат в http://localhost:8081/

1 Ответ

0 голосов
/ 05 ноября 2019

вы реализовали store rollbackRemoveItem?

Возможно, вы могли бы использовать что-то похожее на это:

connect() {
  const connection = getModule(Connection, this.$store);
  connection.testAction();
  this.$store.dispatch('Connection/rollbackRemoveItem');
}
...