Подтвердить что ты не робот

Knockout.js, похоже, сбивает мои обработчики событий jQuery, как грубо

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

Я использую Knockout.js для своего пользовательского интерфейса, который отлично работает сам по себе. Тем не менее, я пытаюсь использовать сторонний код, который делает выпадающие списки и флажки выглядят красиво. На самом деле я даже не уверен, что это библиотека сторонних разработчиков или что-то, что написано нашими дизайнерами. Этот код скрывает реальный флажок и заменяет его поддельным <span />, который имитирует флажок через CSS. Событие click диапазона запускает событие change реального флажка:

// this code updates the fake UI
this._changeEvent = function() {
    self.isChecked = self.$input.is(':checked');
    self._updateHTML(false, true);
    jQuery(self).trigger('change');
};

// when the user clicks the fake checkbox, we trigger change on the real checkbox
this.$fake.on('click', function(e) {
    e.preventDefault();
    self.$input.click().trigger('change');
});

// Bind _changeEvent to the real checkbox
this.$input.change(this._changeEvent);

Это действительно работает с Knockout.js, так как Knockout будет слушать этот обработчик событий. Другими словами, когда пользователь нажимает фальшивый флажок, обновляется связанная модель нокаута. Однако то, что не работает, - это обновление модели. Если я вызываю:

model.SomeValue(!curValue); // SomeValue is bound to a checkbox, flip its value

Модель обновляется, но поддельный пользовательский интерфейс не обновляется. Я проследил эту проблему до кода в ko.bindingHandlers.checked.update, который выполняет следующие действия:

// When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
element.checked = value;

В принципе, свойство element.checked установлено, но никаких событий не запускается. Таким образом, функция _changeEvent никогда не вызывается. Итак, я реализовал свою собственную функцию ko.bindingHandlers.checked.update, которая является копией встроенного. Теоретически это все, что мне нужно сделать:

   ko.bindingHandlers.checked.update = function (element, valueAccessor)
   {
      var value = ko.utils.unwrapObservable(valueAccessor());

      if (element.type == "checkbox")
      {
         if (value instanceof Array)
         {
            // When bound to an array, the checkbox being checked represents its value being present in that array
            element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
         }
         else
         {
            // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
            //element.checked = value;
            $(element).prop('checked', value).trigger('change'); // <--- this should work!
         }
      }
      else if (element.type == "radio")
      {
         element.checked = (element.value == value);
      }
   };

Вместо того, чтобы устанавливать element.checked, я вместо этого вызываю .prop('checked', value) и запускаю событие изменения. Однако это не работает. Вот что я знаю до сих пор:

  • Если я удалю Knockout.js из уравнения, $(element).prop('checked', value).trigger('change'); работает отлично. Итак, knockout.js прикручивает событие каким-то образом. Не отвязывает ли этот обработчик событий?
  • Я подтвердил, что $(element) - это то же самое, что и this.$input в фальшивом коде привязки кода. Я могу установить другие свойства expando на этот элемент, и они появятся.
  • Я пробовал несколько подходов, чтобы попытаться отладить в Knockout.js и jQuery, чтобы увидеть, все еще связано событие, однако я ничуть не с этим подходом. Моя догадка в том, что Knockout.js каким-то образом заменил обработчик события change своим собственным внутренним, и существующие привязки были удалены. Я еще не нашел способа подтвердить это.

Мой вопрос: В основном, я ищу решение этой проблемы. Does Knockout.js удаляет существующие события change, которые были там до применения модели? Каковы следующие шаги по отладке этого кода и выяснению, что происходит?

4b9b3361

Ответ 1

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

Вот пример модели и пользовательской привязки для обработки обновления пользовательского интерфейса:

var Model = function () {
    this.checked = ko.observable(false);
};

ko.bindingHandlers.customCheckbox = {
    init: function (element) {
        // Create the custom checkbox here
        $(element).customInput();
    },
    update: function (element) {
        // Update the checkbox UI after the value in the model changes
        $(element).trigger('updateState');
    }
};

Я привяжу модель к следующему HTML:

<input type="checkbox" name="genre" id="check-1" value="action" data-bind="checked: checked, customCheckbox: checked" />

И действительно, все, что мне нужно будет сделать.

Вот пример: http://jsfiddle.net/badsyntax/4Cy3y/

[edit] - я думаю, что я бросился читать ваш вопрос и не понял суть вашего вопроса. В любом случае я оставлю свой ответ.

Ответ 2

Попробуйте запустить и прослушать также пользовательское событие:

element.checked = value;
element.dispatchEvent(new Event("forcedChange"));

Ответ 3

Попробуйте создать объект checkbox как плагин jQuery и создать настраиваемые привязки для нокаута, чтобы привязать его к вашей модели viewmodel. Я установил jsfiddle, который делает именно это: http://jsfiddle.net/markhagers/zee4z/3/

$(function () {
    var viewModel = {
        truthyValue1: ko.observable(false),
        truthyValue2: ko.observable(true)
    };

    ko.applyBindings(viewModel);
});

(мне пришлось добавить этот фрагмент, или мой ответ не был принят, см. jsfiddle для примера с полным кодом). Извините, это похоже на много кода, но часть плагина была изначально автономным плагином jquery, с добавлением привязки нокаута, добавленным позже, некоторый код в нем был устаревшим с помощью привязок нокаута. Его можно поместить в собственный файл, чтобы уменьшить беспорядок. Создание пользовательских привязок зарегистрировано на веб-сайте нокаута.

Ответ 4

Просто мозговой штурм, но был ли этот сторонний контроль разработан с использованием пространств имен событий? Возможно, вы можете предположить, что нокаут и этот контроль каким-то образом борются с событием "изменение" по умолчанию. Когда я разрабатываю виджеты и приложения, я всегда использую пространства имен событий при связывании событий с помощью jQuery.

Пример использования пространства имен 'myWidget':

$('#element').on('change.myWidget', function(e) {
  // handle the event
}

Я мог бы быть полностью вне базы.

Ответ 5

Просто подумайте. Кажется, что ваши дизайнеры запускают событие click и change в этой строке:

self.$input.click().trigger('change');

Не может быть что-то попробовать? Может быть, это связано с событием click, а не с событием изменения? Попробуйте это в инструкции else:

$(element).prop('checked', value).click().trigger('change'); // <--- this should work!

Что я понял, что .click() делает: он имитирует mouseclick и в то же время запускает событие click (Duh?).

Я знаю, что иногда (например, когда в радио или флажке) некоторые кодеры привязывают событие к клику, а не к изменению, поскольку это скорее срабатывает в правильное время (в IE7 событие изменения срабатывает в действительно странном путь).

Просто мысль, может быть, что-то попробовать. Надеюсь, это поможет!

EDIT: Я видел ответ о пространствах имен в ваших событиях и пользовательских событиях. Это также может помочь вам, надеюсь, что это не слишком большие изменения.

/Дж.

Ответ 6

Похоже, проблема, с которой вы сталкиваетесь, связана не с конфликтом в событиях с нокаутом и другим набором кода (например, с изменением CSS флажков), а из-за конфликта в том, как поток Javascript и события в потоке обрабатываются.

Javascript обрабатывается в браузере как один поток: все процессы запускаются последовательно, и любое событие, которое прерывает последовательность, может привести к сбою вашего события. Если вы можете вызывать каждую из функций независимо, но есть помехи, когда вы включаете оба в свой script, событие onclick или onmousedown, которое вызывает изменение CSS для флажков, может помешать нокауту script. В этом "единственном потоке" все события могут влиять на выполнение другого, даже движения мыши.

Этот пример (http://ejohn.org/blog/how-javascript-timers-work/) демонстрирует эту идею с использованием таймеров, но я думаю, что ваша текущая ситуация может быть связана с одной и той же проблемой.

Чтобы решить эту проблему, вы можете написать короткую функцию, например:

функция doBoth (checkboxid) {

$( "# checkboxid" ) addclass ( "prettychecked" );.

     

$( "# checkboxid" ). prop ('checked', true);

}

Важным моментом является то, что каждое действие происходит последовательно, без прерывания события мыши.

Надеюсь, это поможет!