【发布时间】:2012-09-25 04:12:38
【问题描述】:
我非常喜欢 Eric Barnard's knockout validation lib 与 observables 集成、允许分组并提供自定义验证器可插入性(包括即时验证器)的方式。有几个地方可以让用户体验更加灵活/友好,但总体而言,它的文档记录得当...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);
关于上面代码的几点说明:有 2 个独立的验证组,每个嵌套模型一个。嵌套模型 #1 没有异步验证器,嵌套模型 #2 同时具有同步(必需)和异步。异步调用服务器调用来验证输入。当服务器响应时,callback 参数用于告诉ko.validation 用户输入是好是坏。 如果您在指示的行上放置断点并使用已知的无效值触发验证,您最终会陷入无限循环,其中 ajax success 函数会导致再次调用 validator 函数。 I破解了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;
}
这个函数在一个订阅链中,附加到用户输入的 observable 中,所以当它的值改变时,新的值将被验证。该算法在每个附加到输入的验证器上循环,并根据验证器是否异步执行单独的函数。如果同步验证失败,则循环中断并且整个validateObservable 函数退出。如果所有同步验证器都通过,则执行最后 3 行,本质上是告诉 ko.validation 此输入有效。库中的__valid__ 函数如下所示:
//the true holder of whether the observable is valid or not
observable.__valid__ = ko.observable(true);
有两点需要注意:__valid__ 是一个 observable,在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__ observable 设置为true 并退出之前,只执行了这个函数的第一行和最后一行。 callBack 函数作为第三个参数传递给MyViewModel 中声明的异步validator 函数。然而在此之前,isValidating observable 的订阅者被调用以通知异步验证已经开始。当服务器调用完成时,回调被调用(在这种情况下只是传递 true 或 false)。
现在,当服务器端验证失败时,MyViewModel 中的断点会导致无限乒乓循环:在上面的 callBack 函数中,请注意验证失败时 __valid__ observable 是如何设置为 false 的。以下是发生的事情:
- 无效的用户输入改变了
nestedModel2.prop2observable。 -
ko.validation.validateObservable会通过订阅收到此更改的通知。 -
validateAsync函数被调用。 - 调用自定义异步验证器,向服务器提交异步
$.ajax调用并退出。 -
ko.validation.validateObservable将__valid__observable 设置为true并退出。 - 服务器返回无效响应,并执行
callBack(false)。 -
callBack函数将__valid__设置为false。 -
ko.validation.validateObservable被通知__valid__可观察对象的更改(callBack将其从true更改为false)这基本上重复了上面的步骤 2。 - 重复上述步骤 3、4 和 5。
- 由于 observable 的值没有改变,服务器返回另一个无效响应,触发上面的步骤 6、7、8 和 9。
- 我们自己有一场乒乓球比赛。
所以问题似乎在于ko.validation.validateObservable 订阅处理程序不仅在监听用户输入值的变化,而且还在监听其嵌套的__valid__ observable 的变化。这是一个错误,还是我做错了什么?
次要问题
您可以从上面的ko.validation 来源中看到,带有异步验证器的用户输入值在服务器对其进行验证时被视为有效。正因为如此,打电话nestedModel2.isValid() 不能依赖“真相”。相反,看起来我们必须使用isValidating 挂钩来创建对异步验证器的订阅,并且只有在它们通知false 的值后才能做出这些决定。这是设计使然吗?与库的其他部分相比,这似乎是最反直觉的,因为 non 异步验证器没有要订阅的 isValidating,并且 可以 依赖 .isValid()说实话。这也是设计使然,还是我在这里也做错了什么?
【问题讨论】:
-
您为这个问题付出了很多努力,很抱歉看到您没有任何回复。我只是 KO 验证的新手,所以我真的帮不了你。
-
这个问题有一些问题。提出的实际问题尚不清楚,并且隐藏在外部链接中。在意识到这一点之前,我必须阅读几段文字。这个问题有太多的上下文;只见树木不见森林。你似乎在问多个问题。建议:在 JSFiddle 中创建最简单的问题重现,在您的问题中链接到它,并在第一段中提出一个简洁的问题。这样做,您将更有可能得到一些答案。
-
您能否更新或回答您的问题?我看到你已经从这个网址想出了一个解决方案......github.com/ericmbarnard/Knockout-Validation/issues/145。您的回答将帮助很多人,因为那里没有太多关于异步验证的信息。
标签: javascript asynchronous knockout.js knockout-2.0 knockout-validation