См. редактирование внизу.
У моей компании огромная кодовая база, и мы хотим начать использовать нокаут более эффективно. Однако у нас уже есть код проверки, который заботится обо всех аспектах проверки на стороне клиента. Он использует jQuery для отображения сообщений об ошибках проверки и для очистки пользовательского ввода.
Например, если я добавлю класс «validate-range» к входным данным, он будет использовать события jQuery change/focusout для отслеживания изменений, а затем, если значение выходит за пределы диапазона, оно заменит его значением min/max, используя $(ввод).val(). Поскольку этот код проверки вносит изменения таким образом программно, моя модель нокаутирующего представления не будет обновляться при внесении таких изменений.
Этот код проверки используется везде в системе и в настоящее время не может быть заменен, поэтому, чтобы использовать нокаут, я должен заставить его работать вместе с этим кодом. До сих пор я пытался создать пользовательскую привязку значения, которая добавляет дополнительный обработчик событий изменения, который используется для обновления модели представления всякий раз, когда код проверки изменяет значение ввода.
Это работает на удивление хорошо во всех случаях, кроме как внутри привязки foreach (что, как мне кажется, аналогично использованию шаблона/с привязкой). Мой обработчик событий изменения не запускается ни для каких входных данных внутри foreach, которые используют пользовательскую привязку значения, даже несмотря на то, что пользовательская привязка повторно применяется ко всем входным данным внутри foreach каждый раз, когда изменяется наблюдаемый массив.
Я надеялся, что кто-то сталкивался с этой проблемой раньше, когда ему приходилось работать с нокаутом с существующим кодом javascript, который изменяет значения DOM и, следовательно, не обновляет модель представления. Любая помощь приветствуется.
Код Javascript для пользовательской привязки, создания модели представления и старого кода проверки:
// custom value binding for amounts
ko.bindingHandlers.amountValue = {
init: function (element, valueAccessor) {
var underlyingObservable = valueAccessor(),
interceptor = ko.computed({
read: function () {
var value = underlyingObservable();
return formatAmount(value);
},
write: function (newValue) {
var current = underlyingObservable(),
valueToWrite = parseAmount(newValue);
if (valueToWrite !== current)
underlyingObservable(valueToWrite);
else if (newValue !== current.toString())
underlyingObservable.valueHasMutated();
}
});
// i apply a change event handler when applying the bindings which calls the write function of the interceptor.
// the intention is to have the change handler be called anytime the old validation code changes an input box's value via
// $(input).val("new value"); In the case of the foreach binding, whenever the observable array changes, and the table rows
// are re-rendered, this code does get ran when re-applying the bindings, however the change handler doesn't get called when values are changed.
ko.applyBindingsToNode(element, { value: interceptor, event: { change: function () { interceptor($(element).val()); } } });
}
};
// view model creation
// auto create ko view model from json sent from server
$(function () {
viewModel = ko.mapping.fromJS(jsonModel);
ko.applyBindings(viewModel);
});
// old validation code
$(document).on("focusout", ".validate-range", function () {
var $element = $(this),
val = $element.val(),
min = $element.attr("data-val-range-min"),
max = $element.attr("data-val-range-max");
if (val < min)
// my change handler from custom binding doesn't fire after this to update view model
$element.val(min);
if (val > max)
// my change handler from custom binding doesn't fire after this to update view model
$element.val(max);
// more code to show error message
});
Код HTML, который использует пользовательскую привязку внутри привязки foreach:
<table>
<thead>
<tr>
<td>Payment Amount</td>
</tr>
</thead>
<tbody data-bind="foreach: Payments">
<tr>
<td><input type="text" class="validate-range" data-val-range-min="0" data-val-range-max="9999999" data-bind="amountValue: Amount" /></td>
</tr>
</tbody>
</table>
Таким образом, в приведенном выше примере, если я ввожу «-155» в текстовое поле суммы, моя пользовательская привязка запускается и устанавливает сумму модели представления на -155. Затем запускается старая проверка и повторно устанавливается значение текстового поля на «0» с помощью $(input).val(0). Моя модель представления не обновляется на данный момент и по-прежнему отражает значение -155. Мой обработчик событий изменения из пользовательской привязки должен запускаться для обновления модели представления до 0, но это не так.
Редактировать:
Как указано в ответе, .val() не вызывает никаких событий изменения. Обработчик события изменения, который я добавил, ничего не сделал. Причина, по которой модель представления обновлялась, когда код проверки изменил значение вне привязки foreach, заключалась в том, что у нас была логика где-то еще в нашем коде javascript, которая вручную запускала событие изменения с использованием события размытия, которое, в свою очередь, запускало мою пользовательскую привязку. для запуска и обновления модели представления. Этот обработчик событий размытия был напрямую привязан к текстовым полям, а не делегирован, поэтому он работал для текстовых полей, которые были там при первом отображении страницы, но не для тех, которые были динамически вставлены привязкой foreach.
На данный момент я просто изменил эту логику, чтобы делегировать события внутри документа, чтобы он включал динамически вставляемые текстовые поля, и, похоже, он работает нормально. Я надеюсь придумать лучшее решение в будущем.