当另一个计算的可观察量发生变化时,触发一个可观察量的自定义扩展器

firing custom extender of one observable when another computed observable is changed

我正在尝试在不使用敲除验证库的情况下创建自己的验证。我正在尝试创建一个通用的验证扩展程序,它可以执行我希望它执行的所有类型的验证。我通过将对象中的验证类型和所需标志传递给扩展程序来做到这一点。问题在于验证方法仅在密码字段更改时触发,而不是在更改 PasswordVisible 属性 时触发。当密码已经为空并且更改 PasswordVisible 属性 时,这会导致问题,尝试清空密码不会被视为更改,因此不会触发扩展程序。

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <script type="text/javascript" src="knockout-3.4.0.js"></script>

    Name:<input type="text" data-bind="value:Name" /><br />
    Already A User: <input type="checkbox" data-bind="checked:AlreadyUser" /><br />
    New Password:<input type="password" data-bind="value:Password,visible:PasswordVisible" /><br />
    <input type="button" value="Submit" onclick="validateModel();" />

    <script type="text/javascript" >
        var pageModel;

        ko.extenders.Validate = function (target, validateOptions) {
            target.HasErrors = ko.observable(false);
            var required = validateOptions.required();
            var validationType = validateOptions.validationType;
            function validate(newValue) {
                alert('validating');
                if (required) {
                    switch (validationType) {
                        case "Text":
                            target.HasErrors(newValue == "" ? false : true);
                            break;
                        default:
                            target.HasErrors(false);
                            break;
                    }
                }
            }

            validate(target());
            target.subscribe(validate);
            return target;
        };

        //The model itself
        var ViewModel = function () {            
            var self = this;
            self.Name = ko.observable('');
            self.AlreadyUser = ko.observable(false);
            //computed variable that sets the visibility of the password field. I have to clear the password when am making it invisible
            self.PasswordVisible = ko.computed(function () { return !this.AlreadyUser(); }, this).extend({ notify: 'always' });
            //this field is only required when visible
            self.Password = ko.observable('').extend({ Validate: { required: function () { return self.PasswordVisible() }, validationType: "Text" } });
            self.PasswordVisible.subscribe(function (newVal) { self.Password(''); });
            self.HasErrors = ko.computed(function () { return self.Password.HasErrors(); },self);
        };



        //The method calls on click of button
        function validateModel() {
            alert(pageModel.HasErrors());
            }

        //create new instance of model and bind to the page
        window.onload = function () {          
            pageModel = new ViewModel();
            ko.applyBindings(pageModel);
        };

    </script>
</body>
</html>

如何在更改 PasswordVisible 时触发验证。

您可以将 HasErrors 设为 ko.computed 以自动创建对任何使用的可观察对象的订阅。它可能会触发一些不必要的重新评估...

ko.extenders.Validate = function(target, validateOptions) {
  target.HasErrors = ko.computed(function() {
    // Create subscription to newValue
    var newValue = target();

    // Create subscriptions to any obs. used in required
    var required = validateOptions.required();
    var validationType = validateOptions.validationType;

    if (ko.unwrap(required)) {
      switch (validationType) {
        case "Text":
          return newValue == "";
      }
    };


    return false;
  }, null, {
    deferEvaluation: true
  });

  return target;
};

请注意,您也不需要将 PasswordVisible observable 包装在一个函数中来执行它;您可以改用 ko.unwrap

这是我在您的代码中的方法。一旦您在密码中有一个值时隐藏了密码,您可能想再看一下多重验证(通过 self.Password('') 清除会触发另一次验证)。

var pageModel;
var i = 0;
ko.extenders.Validate = function(target, validateOptions) {
  target.HasErrors = ko.computed(function() {
    console.log("validating " + ++i);

    // Create subscription to newValue
    var newValue = target();

    // Create subscriptions to any obs. used in required
    var required = validateOptions.required();
    var validationType = validateOptions.validationType;

    if (ko.unwrap(required)) {
      switch (validationType) {
        case "Text":
          return newValue == "";
      }
    };


    return false;
  }, null, {
    deferEvaluation: true
  });

  return target;
};

//The model itself
var ViewModel = function() {
  var self = this;
  self.Name = ko.observable('');
  self.AlreadyUser = ko.observable(false);
  //computed variable that sets the visibility of the password field. I have to clear the password when am making it invisible
  self.PasswordVisible = ko.computed(function() {
    return !this.AlreadyUser();
  }, this).extend({
    notify: 'always'
  });
  //this field is only required when visible
  self.Password = ko.observable('').extend({
    Validate: {
      required: function() {
        return self.PasswordVisible()
      },
      validationType: "Text"
    }
  });
  self.PasswordVisible.subscribe(function(newVal) {
    self.Password('');
  });
  self.HasErrors = ko.computed(function() {
    return self.Password.HasErrors();
  }, self);
};



//The method calls on click of button
function validateModel() {
  console.log(pageModel.HasErrors());
}

//create new instance of model and bind to the page
window.onload = function() {
  pageModel = new ViewModel();
  ko.applyBindings(pageModel);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

Name:
<input type="text" data-bind="value:Name" />
<br />Already A User:
<input type="checkbox" data-bind="checked:AlreadyUser" />
<br />New Password:
<input type="password" data-bind="value:Password,visible:PasswordVisible" />
<br />
<input type="button" value="Submit" onclick="validateModel();" />