我真的很喜欢如何Eric Barnard 的淘汰赛验证库 https://github.com/Knockout-Contrib/Knockout-Validation与可观察量集成,允许分组,并提供自定义验证器可插拔性(包括动态验证器)。有几个地方可以使用户体验更加灵活/友好,但总的来说,它有相当详细的文档记录......在我看来,除了异步验证器 https://github.com/Knockout-Contrib/Knockout-Validation/wiki/Async-Rules.
今天我在进行搜索之前花了几个小时来解决这个问题登陆此 https://github.com/Knockout-Contrib/Knockout-Validation/issues/145. I think我和原作者有同样的问题,但同意目前尚不清楚 duxa 到底要求什么。我想让这个问题引起更多的关注,所以我也在这里问。
function MyViewModel() {
var self = this;
self.nestedModel1.prop1 = ko.observable().extend({
required: { message: 'Model1 Prop1 is required.' },
maxLength: {
params: 140,
message: '{0} characters max please.'
}
});
self.nestedModel2.prop2 = ko.observable().extend({
required: { message: 'Model2 Prop2 is required' },
validation: {
async: true,
validator: function(val, opts, callback) {
$.ajax({ // BREAKPOINT #1
url: '/validate-remote',
type: 'POST',
data: { ...some data... }
})
.success(function(response) {
if (response == true) callback(true); // BREAKPOINT #2
else callback(false);
});
},
message: 'Sorry, server says no :('
}
});
}
ko.validation.group(self.nestedModel1);
ko.validation.group(self.nestedModel2);
关于上面代码的一些注意事项:有 2 个独立的验证组,每个验证组对应一个嵌套模型。嵌套模型 #1 没有异步验证器,嵌套模型 #2 有同步(必需)和异步。异步调用服务器调用来验证输入。当服务器响应时,callback
论证是用来告诉ko.validation
用户输入是好还是坏。如果您在指示的行上放置断点并使用已知的无效值触发验证,则最终会出现无限循环,其中 ajaxsuccess
函数导致validator
函数被再次调用。我破解了ko.validation
来源查看发生了什么事。
ko.validation.validateObservable = function(observable) {
// set up variables & check for conditions (omitted for brevity)
// loop over validators attached to the observable
for (; i < len; i++) {
if (rule['async'] || ctx['async']) {
//run async validation
validateAsync();
} else {
//run normal sync validation
if (!validateSync(observable, rule, ctx)) {
return false; //break out of the loop
}
}
}
//finally if we got this far, make the observable valid again!
observable.error = null;
observable.__valid__(true);
return true;
}
该函数位于附加到用户输入可观察对象的订阅链中,以便当其值发生变化时,新值将得到验证。该算法循环访问附加到输入的每个验证器,并根据验证器是否异步执行单独的函数。如果同步验证失败,循环就会被破坏,整个validateObservable
函数退出。如果所有同步验证器都通过,则执行最后 3 行,本质上是告诉ko.validation
该输入有效。这__valid__
库中的函数如下所示:
//the true holder of whether the observable is valid or not
observable.__valid__ = ko.observable(true);
从中可以得到两点:__valid__
是一个可观察量,并且它被设置为true
之后validateAsync
函数退出。现在我们来看看validateAsync
:
function validateAsync(observable, rule, ctx) {
observable.isValidating(true);
var callBack = function (valObj) {
var isValid = false,
msg = '';
if (!observable.__valid__()) {
// omitted for brevity, __valid__ is true in this scneario
}
//we were handed back a complex object
if (valObj['message']) {
isValid = valObj.isValid;
msg = valObj.message;
} else {
isValid = valObj;
}
if (!isValid) {
//not valid, so format the error message...
observable.error = ko.validation.formatMessage(...);
observable.__valid__(isValid);
}
// tell it that we're done
observable.isValidating(false);
};
//fire the validator and hand it the callback
rule.validator(observable(), ctx.params || true, callBack);
}
需要注意的是,之前只执行了该函数的第一行和最后一行ko.validation.validateObservable
设置__valid__
observable 为 true 并退出。这callBack
function 是作为第三个参数传递给 async 的函数validator
函数声明于MyViewModel
。然而在此之前,一个isValidating
调用 observable 的订阅者来通知异步验证已经开始。当服务器调用完成时,将调用回调(在本例中仅传递 true 或 false)。
现在这就是为什么断点MyViewModel
当服务器端验证失败时,会导致无限的乒乓循环:callBack
上面的函数,注意如何__valid__
当验证失败时 observable 设置为 false。发生的情况如下:
- 无效的用户输入改变了
nestedModel2.prop2
可观察到的。
- The
ko.validation.validateObservable
通过订阅此更改来通知。
- The
validateAsync
函数被调用。
- 调用自定义异步验证器,该验证器提交异步
$.ajax
调用服务器并退出。
- The
ko.validation.validateObservable
设置__valid__
可观察到true
并退出.
- 服务器返回无效响应,并且
callBack(false)
被执行。
- The
callBack
功能集__valid__
to false
.
- The
ko.validation.validateObservable
被通知更改__valid__
可观察到的(callBack
改变它从true
to false
) 这基本上重复了上面的步骤 2。
- 重复上述步骤 3、4 和 5。
- 由于 observable 的值没有改变,服务器返回另一个无效响应,触发上面的步骤 6、7、8 和 9。
- 我们自己有一场乒乓球比赛。
所以看来问题是ko.validation.validateObservable
订阅处理程序不仅监听用户输入值的更改,还监听其嵌套值的更改__valid__
可观察到的。这是一个错误,还是我做错了什么?
一个次要问题
你可以从ko.validation
上面的来源表明,当服务器验证它时,带有异步验证器的用户输入值被视为有效。正因为如此,调用nestedModel2.isValid()
不能依赖“真相”。相反,看起来我们必须使用isValidating
挂钩来创建对异步验证器的订阅,并且仅在通知值后才做出这些决定false
。这是设计使然吗?与图书馆的其他部分相比,这似乎是最违反直觉的,因为non异步验证器没有isValidating
订阅,以及can依靠.isValid()
说实话。这也是设计使然,还是我在这里也做错了什么?