Почему здесь возникает проблема при создании нового массива вместо опустошения старого? - PullRequest
0 голосов
/ 02 августа 2020

Я работаю над проектом в Javascript и столкнулся с проблемой, которую не понимаю. В основном я написал генератор карт, который генерирует карты из шума, этот бит в порядке и работает. Я хочу, чтобы конечный пользователь мог редактировать эту карту, размещая то, что я называю действиями (в основном формы, которые влияют на генерацию основного шума), и система их редактирования вызывает проблемы.

Я использую Mithril JS для обновления пользовательского интерфейса для редактирования свойств действий, и он в основном работает. Часть того, что мне нужно сделать, - это щелкнуть действие и выбрать его, это отобразится в пользовательском интерфейсе, чтобы вы могли его редактировать. Выбор действия работает, так же как отмена выбора или выбор другого действия, но если я затем удалю действие, оно больше не будет отображаться в пользовательском интерфейсе. Вот соответствующий фрагмент кода:

var editModeElements = {
    actions: editedActions.actions,
    selectedAction: [],
    selectedPoint: null,
    update: function() {
        this.actions = editedActions;
        this.clearSelection();
        m.redraw();
    },
    deleteAction: function(action) {
        for( var i = 0; i < this.actions.actions.length; i++)
        { 
            if ( this.actions.actions[i] == action) 
            { 
                this.actions.actions.splice(i, 1); i--; 
            }
        }
        this.clearSelection();
        unsavedEdits = true;
    },
    selectAction: function(action) {
        this.selectedAction = new Array();
        this.selectedPoint = null;
        this.selectedAction.push(action);
        m.redraw();
        viewport.redraw();
    },
    clearSelection: function() {
        this.selectedAction = new Array();
        this.selectedPoint = null;
        m.redraw();
        viewport.redraw();
    },
    addAction: function(actionType) {
        var action = createBlankAction(actionType);
        this.actions.actions.push(action);
        this.selectAction(action);
        unsavedEdits = true;
    },
    oncreate: this.update,
    view: function() {
        return m(".action-dialog", [
                m(".action-editor-buttons", [
                    m(".new-action", {onclick: function() {editModeElements.addAction(MASK_TYPE.CIRCLE)}}, "Circle"),
                    m(".new-action", {onclick: function() {editModeElements.addAction(MASK_TYPE.RECTANGLE)}}, "Rectangle"),
                    m(".new-action", {onclick: function() {editModeElements.addAction(MASK_TYPE.POLYGON)}}, "Polygon"),
                    m(".new-action", {onclick: function() {editModeElements.addAction(MASK_TYPE.LINE)}}, "Line"),
                    m(".new-action", {onclick: function() {editModeElements.addAction(MASK_TYPE.COMPLEX_CIRCLE)}}, "Complex Circle"),
                    m(".new-action", {onclick: function() {editModeElements.addAction(MASK_TYPE.COMPLEX_RECTANGLE)}}, "Complex Rectangle"),
                    m(".new-action", {onclick: function() {editModeElements.addAction(MASK_TYPE.COMPLEX_POLYGON)}}, "Complex Polygon"),
                    m(".new-action", {onclick: function() {editModeElements.addAction(MASK_TYPE.COMPLEX_LINE)}}, "Complex Line"),
                ]),
                createActionEditorList(this.selectedAction, this),
        ]);
    }
}

Надеюсь, этого достаточно, чтобы сказать мне, где я ошибаюсь. Проблема как-то связана с созданием нового массива, когда я вызываю clearSelection. Если я удалю этот вызов из deleteSelection, проблема исчезнет (но удаленный элемент останется выбранным в пользовательском интерфейсе, что нежелательно) или если я изменю часть в clearSelection, чтобы вытолкнуть элемент вместо создания нового массива, он работает .

Однако, и это меня смущает, clearSelection отлично работает (новый массив или метод pop), когда я использую его для отмены выбора, щелкая за пределами действия, и я также создаю новый массив в selectAction что не вызывает проблем. Он перестает работать только после вызова deleteSelection. Массив, из которого удаляется элемент в deleteSelection, особо не связан с массивом selectedAction, кроме действия, которое выбрано из него, поэтому я не вижу, что это может вызвать эти проблемы. Я также изменил его с filter (который создаст новый массив) на splice (который не работает), чтобы увидеть, помогло ли это, но без изменений. Действия по-прежнему выбираются после удаления и изменения цвета, их также можно успешно перетащить и сохранить, поэтому проблема, очевидно, связана с подключением к пользовательскому интерфейсу.

Я не думаю, что что-то еще за пределами этого фрагмента имеет значение он в значительной степени самодостаточен, функция createActionEditorList в конце просто превращает действия в HTML элементы для пользовательского интерфейса, работает и довольно долго, поэтому я не включил ее.

Почему создание нового Массив сломал мой код?

1 Ответ

1 голос
/ 03 августа 2020

Mithril. js теперь рекомендует использовать замыкания для компонентов с отслеживанием состояния. Я бы порекомендовал перейти на этот стиль, потому что гораздо проще понять, где находится состояние.

Документация Mithril с описанием компонентов закрытия :

В В приведенных выше примерах каждый компонент определяется как POJO (Plain Old JavaScript Object), который используется Mithril внутри как прототип для экземпляров этого компонента. Можно использовать состояние компонента с помощью POJO (как мы обсудим ниже), но это не самый чистый или простой подход.

Что касается возникшей у вас ошибки, после некоторого тестирования я думаю, что она связано с тем, что this, который вы используете в своих методах, не то же самое, что editModeElements. Насколько я понимаю, mithril использует POJO как прототип для this, используемых в методах. Я думаю, что, возможно, при сокращении вашего кода некоторые части были изменены, из-за чего ошибка возникает для меня немного по-другому, когда я пытаюсь запустить ее. Суть проблемы в том, что this! = editModeElements, и это действительно сбивает код с толку. Иногда вы можете изменять прототип, а иногда - экземпляр.

Некоторые проблемы:

  1. Когда вы устанавливаете oncreate: this.update, this не editModeElements но это в текущей области. Так что, скорее всего, this.update не определено, а для oncreate установлено значение undefined и никогда не вызывается.
  2. this, переданный в createActionEditorList, не тот же объект, что editModeElements.addAction, используемый в editModeElements.addAction.
  3. У вас есть некоторые методы, относящиеся к actions.actions, но editModeElements инициализируется таким образом, что actions: editedActions.actions.

Трудно сказать, что еще происходит, без остальной части вашего кода . Сначала я попытался использовать POJO для состояния, потому что я думал, что это необходимо, но замыкания легче понять. Возможно, вы могли бы уточнить, нужен ли вам этот стиль по какой-то причине, потому что его очень сложно надежно реализовать.

Вот что, я думаю, вы хотите, чтобы ваш компонент переписал с использованием стиля замыкания, мне пришлось заглушить несколько вещи, чтобы заставить его работать,:

// STUB
var MASK_TYPE = {
  CIRCLE: "CIRCLE",
  RECTANGLE: "RECTANGLE",
  POLYGON: "POLYGON",
  LINE: "LINE",
  COMPLEX_CIRCLE: "COMPLEX_CIRCLE",
  COMPLEX_RECTANGLE: "COMPLEX_RECTANGLE",
  COMPLEX_POLYGON: "COMPLEX_POLYGON",
  COMPLEX_LINE: "COMPLEX_LINE",
};

function createBlankAction(actionMask) {
  // STUB
  return {mask: actionMask};
}

var EditModeElements = function () {
  var actions = [],
      selectedAction = [],
      selectedPoint = null,
      unsavedEdits = false;
  function deleteAction(action) {
      for( var i = 0; i < actions.length; i++) { 
          if (actions[i] == action)  { 
              actions.splice(i, 1); i--; 
          }
      }
      clearSelection();
      unsavedEdits = true;
  }
  function selectAction(action) {
      selectedAction.length = 0;
      selectedPoint = null;
      selectedAction.push(action);
      //?viewport.redraw();
  }
  function clearSelection() {
      selectedAction.length = 0;
      selectedPoint = null;
      //?viewport.redraw();
  }
  function addAction(actionType) {
    var action = createBlankAction(actionType);
    actions.push(action);
    selectAction(action);
    unsavedEdits = true;
  }      
  return {
    view: function() {
        return m(".action-dialog", [
                m(".action-editor-buttons", [
                    m(".new-action", {onclick: function() {addAction(MASK_TYPE.CIRCLE)}}, "Circle"),
                    m(".new-action", {onclick: function() {addAction(MASK_TYPE.RECTANGLE)}}, "Rectangle"),
                    m(".new-action", {onclick: function() {addAction(MASK_TYPE.POLYGON)}}, "Polygon"),
                    m(".new-action", {onclick: function() {addAction(MASK_TYPE.LINE)}}, "Line"),
                    m(".new-action", {onclick: function() {addAction(MASK_TYPE.COMPLEX_CIRCLE)}}, "Complex Circle"),
                    m(".new-action", {onclick: function() {addAction(MASK_TYPE.COMPLEX_RECTANGLE)}}, "Complex Rectangle"),
                    m(".new-action", {onclick: function() {addAction(MASK_TYPE.COMPLEX_POLYGON)}}, "Complex Polygon"),
                    m(".new-action", {onclick: function() {addAction(MASK_TYPE.COMPLEX_LINE)}}, "Complex Line"),
                ]),
                m(ActionEditorList, {actions: actions, onDeleteAction: deleteAction}),
        ]);
    }
  };
}

function ActionEditorList() {
  return {
    view: function (vnode) {
      return m('', vnode.attrs.actions.map(function (a) {
        return m('b', {
          onclick: function () { 
            vnode.attrs.onDeleteAction(a);
          }
        }, a.mask);
      }));
    }
  }
}

m.mount(document.body, EditModeElements);
...