请参阅底部的编辑。
我的公司拥有庞大的代码库,我们希望开始更有效地使用淘汰赛。但是,我们已经有验证代码来处理客户端验证的所有方面。它使用 jQuery 来显示验证错误消息并清理用户输入。
例如,如果我将类“validate-range”添加到输入中,它将使用 jQuery 更改/聚焦事件来跟踪更改,然后如果值超出范围,它将使用最小/最大值替换它$(输入).val()。由于此验证代码以编程方式进行更改,因此在进行此类更改时,我的淘汰视图模型将不会更新。
这个验证码在系统中随处可见,暂时不能替换,所以为了使用knockout,我必须让它和这个代码一起工作。到目前为止,我尝试的是创建一个自定义值绑定,该绑定添加了一个额外的更改事件处理程序,用于在验证代码更改输入值时更新视图模型。
这在所有情况下都非常有效,除了在 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
});
在 foreach 绑定中使用自定义绑定的 HTML 代码:
<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。然后旧验证运行并使用 $(input).val(0) 将文本框的值重新设置为“0”。我的视图模型此时没有更新,仍然反映 -155 值。我的自定义绑定中的更改事件处理程序应该运行以将视图模型更新为 0,但事实并非如此。
编辑:
正如答案中所指出的, .val() 不会触发任何更改事件。我添加的更改事件处理程序没有做任何事情。当验证代码更改 foreach 绑定之外的值时,视图模型被更新的原因是我们的 javascript 代码中的其他地方有逻辑,该逻辑使用模糊事件手动触发更改事件,这反过来又触发了我的自定义绑定运行和更新视图模型。这个模糊事件处理程序直接绑定到文本框,而不是被委托,因此它适用于页面首次呈现时存在的文本框,但不适用于由 foreach 绑定动态插入的文本框。
目前,我只是更改了此逻辑以委托文档中的事件,因此它将包括动态插入的文本框,并且它似乎工作正常。我希望将来能提出更好的解决方案。