Knockout JS:使用改变DOM的旧验证代码

Knockout JS: Working with old validation code that changes the DOM

本文关键字:验证 代码 DOM 改变 JS Knockout      更新时间:2023-09-26

见底部编辑

我的公司有一个巨大的代码库,我们想开始更有效地使用knockout。但是,我们已经有了处理客户端验证的所有方面的验证代码。它使用jQuery来显示验证错误消息并清理用户输入。

例如,如果我添加类"validate-range"到输入,它将使用jQuery change/focusout事件来跟踪更改,然后如果一个值超出了范围,它将使用$(input).val()将其替换为最小/最大值。由于此验证代码以编程方式进行更改,因此当进行此类更改时,我的knockout视图模型将不会更新。

此验证代码在系统中无处不在,目前无法替换,因此为了使用knockout,我必须使其与此代码一起工作。到目前为止,我所尝试的是创建一个自定义值绑定,它添加了一个额外的更改事件处理程序,用于在验证代码更改输入值时更新视图模型。

这在所有情况下都出奇地好,除了在foreach绑定中(我想这与使用模板/with绑定是一样的)。我的change事件处理程序不会在foreach中使用自定义值绑定的任何输入上触发,即使每次可观察数组发生变化时,自定义绑定都会被重新应用到foreach中的所有输入上。

我希望有人以前处理过这个问题,不得不与现有的改变DOM值的javascript代码进行淘汰工作,因此不更新视图模型。如有任何帮助,不胜感激。

用于自定义绑定、创建视图模型和旧验证代码的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代码的其他地方有逻辑,使用blur事件手动触发更改事件,这反过来触发我的自定义绑定来运行和更新视图模型。这个模糊事件处理程序直接绑定到文本框,而不是委托,所以它适用于页面第一次呈现时存在的文本框,但不适用于由foreach绑定动态插入的文本框。

现在,我只是将这个逻辑更改为委派文档中的事件,因此它将包括动态插入的文本框,并且看起来工作得很好。我希望将来能想出一个更好的解决方案。

调用$(element).val("some value");不触发变更事件。

您需要做:$(element).val("some value").change();