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
, которые были там до применения модели? Каковы следующие шаги по отладке этого кода и выяснению, что происходит?
Ответы
Ответ 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);
}
Важным моментом является то, что каждое действие происходит последовательно, без прерывания события мыши.
Надеюсь, это поможет!