我真的很喜欢Eric Barnard's knockout validation lib与可观察性的集成,允许分组,并提供自定义验证器的可插入性(包括动态验证器)。有几个地方它可以更灵活/更友好,但总的来说,它是合理的良好记录... except, imo, when it comes to async validators。
今天在做搜索和landing on this之前,我纠结了几个小时。我 * 认为 * 我和原作者有同样的问题,但我同意不清楚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);
关于以上代码的几点注意事项:有两个独立的验证组,每个嵌套模型一个。嵌套模型#1没有异步验证器,而嵌套模型#2有两个同步验证器。(必需的)和一个async。async调用服务器调用来验证输入。当服务器响应时,callback
参数用于告诉ko.validation
用户输入是正确的还是错误的。**如果您在指定的行上放置断点并使用已知的无效值触发验证,您最终会得到一个无限循环,其中 AJAX 1 m2n1x函数导致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__
是一个可观测量,在validateAsync
函数退出后设置为true
,现在我们来看一下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__
可观测值设置为true并退出之前,只执行了该函数的第一行和最后一行。callBack
函数是作为第三个参数传递给MyViewModel
中声明的异步validator
函数的函数。然而,在此之前,一个isValidating
可观测对象的订阅者被调用来通知异步验证已经开始。当服务器调用完成时,回调被调用(在这种情况下,只传递true或false)。
下面是当服务器端验证失败时,MyViewModel
中的断点导致无限乒乓循环的原因:在上面的callBack
函数中,请注意当验证失败时,__valid__
可观测值是如何被设置为false的。
1.无效的用户输入更改了nestedModel2.prop2
可观察值。
ko.validation.validateObservable
会透过订阅通知此变更。
1.调用validateAsync
函数。
1.调用自定义异步验证器,该验证器向服务器提交异步$.ajax
调用并退出。ko.validation.validateObservable
将__valid__
可观测量设置为true
并退出。
1.服务器返回无效响应,并执行callBack(false)
。callBack
函数将__valid__
设置为false
。ko.validation.validateObservable
被告知__valid__
可观测量的变化(callBack
将其从true
更改为false
)。这基本上重复了上述步骤2。
1.重复上述步骤3、4和5。
1.由于可观察值没有改变,服务器返回另一个无效响应,触发上面的步骤6、7、8和9。
1.我们有一场乒乓球比赛。
所以看起来问题是ko.validation.validateObservable
订阅处理程序不仅监听用户输入值的更改,而且监听其嵌套__valid__
可观察值的更改。这是一个bug,还是我做错了什么?
次要问题
从上面的ko.validation
源代码中可以看出,当服务器验证时,带有异步验证器的用户输入值被视为有效。因此,调用nestedModel2.isValid()
不能被视为“真实”。相反,看起来我们必须使用isValidating
钩子来创建对异步验证器的订阅,并且只在它们通知false
的值之后才做出这些决定。这是设计的吗?与库的其他部分相比,这似乎是最违反直觉的,因为非异步验证器没有isValidating
可订阅,而且可以依靠.isValid()
来讲述真相。这也是设计使然,还是我在这里也做错了什么?
2条答案
按热度按时间mzillmmw1#
因此,我提出的问题实际上与如何在ko. validation中使用异步验证器有关。从我的经验中,我学到了两大要点:
1.不要创建
async
Anonymous或一次性自定义规则验证器。相反,将它们创建为自定义规则。否则,您将以我的问题中描述的无限循环/ ping ping匹配结束。1.如果你使用
async
验证器,不要信任isValid()
,直到所有async
验证器的isValidating
subscriptions
都变为false。如果您有多个异步验证器,则可以使用如下模式:
您也可以see this for another reference with similar alternate solutions。
下面是一个异步“自定义规则”的示例:
基本上,您可以使用
callback
参数来告诉ko.validation验证是否成功,该调用将触发validated属性观察值上的isValidating
观察值更改回false
(意味着异步验证已经完成,并且现在知道输入是否有效)。如果您的服务器端验证端点返回HTTP 200,则上述方法将起作用(OK)状态。这将导致执行
.done
函数,因为它等效于$.ajax
success
。如果服务器返回HTTP 400(Bad Request)状态,它将触发.fail
函数执行。如果服务器返回一个自定义的验证消息,其中包含400、您可以从xhr.responseText
获得它以有效地覆盖默认you suck because your input was wrong fix it or else
消息。pokxtpni2#
我也遇到了同样的问题,把可观察性和有效性嵌套在一起。在
self.errors = ko.validation.group(self.submissionAnswers, { deep: true, live: true });
中,请注意特殊附加参数:包含字段live: true
的对象