当另一个计算的observable改变时,触发一个observable的自定义扩展器

时间:2016-07-06 10:46:12

标签: knockout.js knockout-validation

我试图在不使用敲除验证库的情况下创建自己的验证。我试图创建一个通用的Validate扩展器,可以执行我希望它做的所有类型的验证。我这样做是通过将验证类型和对象中的必需标志传递给扩展器。我遇到的问题是validate方法仅在Password字段更改时触发,而不是在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时触发验证。

1 个答案:

答案 0 :(得分:1)

您可以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('')触发另一个验证),则隐藏密码后,您可能需要再次查看多个验证。

&#13;
&#13;
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);
};
&#13;
<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();" />
&#13;
&#13;
&#13;