敲除验证 valueUpdate 取决于属性错误

Knockout validation valueUpdate depending on attribute error

是否可以将属性值更新从 "onblur" 更改为 "keyup",具体取决于属性是否附加了错误?

我想模仿 jQuery Validation 中的验证方式,首先对 blur 进行验证,然后对 keyup 进行验证。

这可能吗?

编辑: 让我澄清一下并举个例子。我不介意在 "keyup" 上绑定到模型,我介意的是在用户完成输入之前就显示一条错误消息。相反,如果我以验证电子邮件地址为例。如果用户输入无效的电子邮件,我希望错误显示模糊,如果用户再次将焦点放在该字段上以更正错误,我希望错误在更正后消失。另一方面,如果用户一开始输入的是有效的电子邮件,后来出现错误,则错误应该立即显示。

第二次编辑: 所以我考虑了一下,我认为验证不应该干扰模型绑定,而是应该对错误消息的显示进行更改。如前所述,我希望错误发生后立即出现,但只有在相关字段发生更改事件后才会出现。

我制作了这个 fiddle 几乎 有效,但它应该准确显示我正在努力完成的事情。

http://jsfiddle.net/mntm1bne/3/

<div data-bind="validationOptions: {messageTemplate: 'myCustomTemplate'}">    
    <input data-bind="value: firstName, valueUpdate: 'keyup', event: {change: firstName.enableD}" />
    <br />
    <input data-bind="value: lastName" />

    <div data-bind="if: firstName.isD">
        Firstname is dirty!
    </div>

    <pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
    <div data-bind="text: ko.toJSON($data)"></div>
</div>
<script type="text/html" id="myCustomTemplate">
    <span data-bind="visible: field.isD && !field.isValid(), attr: { title: field.error }">X</span>
</script>

ko.extenders.trackChange = function(target, track) {
  if (track) {
    target.isD = ko.observable(false);
    target.enableD = function() {
        console.log("enable!");
      target.isD(true);
    }
  }
  return target;
};

var ViewModel = function () {
    var self = this;
    self.firstName = ko.observable().extend({ trackChange: true, required: { message: "firstName" }, number: true,
    min: 0,
    max: 100
    });
    self.lastName = ko.observable().extend({ required: { message: "lastName" }});  
}

var viewModel = new ViewModel();

ko.applyBindings(viewModel);

具体来说,错误在于页面加载时显示的第一条验证消息。

为了正确地做到这一点,您需要重写 valueUpdate 绑定,以便它接受一个 observable,并且您的 observable 将基于该值是否有错误。

这听起来很难,所以我选择了一些更骇人听闻的东西。我用 event 绑定替换了 valueUpdate。它在 keydown 时触发,如果验证变量无效,则从输入更新它的值(使用 event.target)。该值将始终在模糊时更新,因此我不必处理它。

var viewModel = {
  num1: ko.observable(50).extend({
    number: true,
    min: 0,
    max: 100
  }),
  maybeEvaluate: function(data, event) {
    setTimeout(function() {
      if (!viewModel.num1.isValid()) {
        viewModel.num1(event.target.value);
      }
    }, 0);
    return true;
  }

};

ko.applyBindings(viewModel);
.validationMessage {
  color: Red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>
0-100:
<input data-bind="value: num1, event: {keydown: maybeEvaluate}" />
<span class="validationMessage" data-bind="validationMessage: num1"></span>
<br />
<input />

这是另一种方法,基于 protected observables。 protected observables 的最初设计是让您可以选择提交或重置更改。我有一个事件强制提交模糊,如果当前值无效,则在接收到新值时将受保护修改为 auto-commit。 (我不需要重置例程,所以我把它去掉了,所以请注意 protectedObservable 定义是自定义的。)

Update 根据您的评论(以及我没有包含所需库的事实),我更新了第二个示例,以便如果该字段有曾经有过错误,变成了continuously-validating.

ko.protectedObservable = function(initialValue) {
  //private variables
  var _actualValue = ko.observable(initialValue),
    _tempValue = initialValue,
    hasHadError = false;

  //computed observable that we will return
  var result = ko.computed({
    //always return the actual value
    read: function() {
      return _actualValue();
    },
    //stored in a temporary spot until commit
    write: function(newValue) {
      _tempValue = newValue;
      if (!result.isValid()) {
        hasHadError = true;
      }
      if (hasHadError) {
        result.commit();
      }
    }
  }).extend({
    notify: "always"
  });

  //if different, commit temp value
  result.commit = function() {
    if (_tempValue !== _actualValue()) {
      _actualValue(_tempValue);
    }
  };

  return result;
};

var viewModel = {
  num1: ko.protectedObservable(50).extend({
    number: true,
    min: 0,
    max: 100
  })
};

ko.applyBindings(viewModel);
.validationMessage {
  color: Red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>
0-100:
<input data-bind="value: num1, valueUpdate: 'input', event: {blur: num1.commit}" />
<span class="validationMessage" data-bind="validationMessage: num1"></span>
<br />
<input />