dan*_*wig 16 javascript asynchronous knockout-2.0 knockout.js knockout-validation
我非常喜欢Eric Barnard的淘汰验证lib与observable集成,允许分组,并提供自定义验证器可插拔性(包括即时验证器).有几个地方可以提供更多的UX灵活性/友好性,但总的来说,它有相当充分的文档...... 除了imo,它涉及到异步验证器.
今天我在这个问题上进行了搜索和着陆之前,我已经与它搏斗了几个小时.我想我和原作者有同样的问题/问题,但同意不清楚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);
Run Code Online (Sandbox Code Playgroud)
关于上面代码的几点注意事项:有两个独立的验证组,每个嵌套模型一个.嵌套模型#1没有异步验证器,嵌套模型#2同时具有同步(必需)和异步.async调用服务器调用来验证输入.当服务器响应时,该callback参数用于ko.validation判断用户输入是好还是坏.如果在指定的行上放置断点并使用已知的无效值触发验证,则最终会出现无限循环,其中ajax success函数会导致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;
}
Run Code Online (Sandbox Code Playgroud)
此函数位于附加到用户输入observable的订阅链中,以便在其值更改时,将验证新值.该算法循环连接到输入的每个验证器,并根据验证器是否异步执行单独的函数.如果同步验证失败,则循环中断,整个validateObservable函数退出.如果所有同步验证器都通过,则执行最后3行,基本上告诉ko.validation该输入有效.该__valid__库中的函数如下所示:
//the true holder of whether the observable is valid or not
observable.__valid__ = ko.observable(true);
Run Code Online (Sandbox Code Playgroud)
从中可以看出两件事:__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);
}
Run Code Online (Sandbox Code Playgroud)
重要的是要注意,在ko.validation.validateObservable将__valid__observable 设置为true并退出之前,只执行此函数的第一行和最后一行.该callBack函数作为第3个参数传递给validator声明的异步函数MyViewModel.但是,在此之前,isValidating会调用一个observable的订阅者来通知异步验证已经开始.服务器调用完成后,将调用回调(在这种情况下只传递true或false).
现在这就是为什么断点在MyViewModel服务器端验证失败时导致无限的乒乓循环的原因:在callBack上面的函数中,注意__valid__验证失败时observable 如何设置为false.这是发生的事情:
nestedModel2.prop2observable.ko.validation.validateObservable通过这种变化的通知订阅.validateAsync函数被调用.$.ajax向服务器提交异步调用并退出.ko.validation.validateObservable 套__valid__可观察到true和退出.callBack(false)执行.callBack功能设置__valid__为false.ko.validation.validateObservable被通知的变化到的__valid__可观察到的(callBack从改变了它true到false)这基本上重复上面的步骤2.所以似乎问题是ko.validation.validateObservable订阅处理程序不仅监听用户输入值的更改,还监视其嵌套的__valid__observable的更改.这是一个错误,还是我做错了什么?
第二个问题
您可以从ko.validation上面的来源中看到,在服务器验证时,具有异步验证器的用户输入值被视为有效.因此,nestedModel2.isValid()不能依赖于"真理".相反,看起来我们必须使用isValidating钩子来创建对异步验证器的订阅,并且只有在它们通知值之后才做出这些决定false.这是设计的吗?与库的其余部分相比,这似乎最直观,因为非异步验证器没有isValidating订阅,并且可以依靠.isValid()说实话.这也是设计,还是我在这里做错了什么?
dan*_*wig 15
所以我问的问题实际上与如何在ko.validation中使用异步验证器有关.我从经验中学到了两个重要的要点:
不要创建async 匿名或单次使用自定义规则验证程序.而是将它们创建为自定义规则.否则你将最终得到我的问题中描述的无限循环/ ping ping匹配.
如果使用async验证器,则isValid()在所有async验证器isValidating subscriptions更改为false 之前不要相信.
如果您有多个异步验证器,则可以使用如下模式:
var viewModel = {
var self = this;
self.prop1 = ko.observable().extend({validateProp1Async: self});
self.prop2 = ko.observable().extend({validateProp2Async: self});
self.propN = ko.observable();
self.isValidating = ko.computed(function() {
return self.prop1.isValidating() || self.prop2.isValidating();
});
self.saveData = function(arg1, arg2, argN) {
if (self.isValidating()) {
setTimeout(function() {
self.saveData(arg1, arg2, argN);
}, 50);
return false;
}
if (!self.isValid()) {
self.errors.showAllMessages();
return false;
}
// data is now trusted to be valid
$.post('/something', 'data', function() { doWhatever() });
}
};
Run Code Online (Sandbox Code Playgroud)
您还可以看到本作与同类替代解决方案的另一个参考.
以下是异步"自定义规则"的示例:
var validateProp1Async = {
async: true,
message: 'you suck because your input was wrong fix it or else',
validator: function(val, otherVal, callback) {
// val will be the value of the viewmodel's prop1() observable
// otherVal will be the viewmodel itself, since that was passed in
// via the .extend call
// callback is what you need to tell ko.validation about the result
$.ajax({
url: '/path/to/validation/endpoint/on/server',
type: 'POST', // or whatever http method the server endpoint needs
data: { prop1: val, otherProp: otherVal.propN() } // args to send server
})
.done(function(response, statusText, xhr) {
callback(true); // tell ko.validation that this value is valid
})
.fail(function(xhr, statusText, errorThrown) {
callback(false); // tell ko.validation that his value is NOT valid
// the above will use the default message. You can pass in a custom
// validation message like so:
// callback({ isValid: false, message: xhr.responseText });
});
}
};
Run Code Online (Sandbox Code Playgroud)
基本上,您使用callbackarg validator函数来告诉ko.validation验证是否成功.该调用将触发isValidating验证属性observables上的observable返回到false(意味着,异步验证已完成,现在知道输入是否有效).
如果验证成功时服务器端验证端点返回HTTP 200(OK)状态,则上述操作将起作用.这将导致.done函数执行,因为它相当于$.ajax success.如果您的服务器在验证失败时返回HTTP 400(错误请求)状态,它将触发.fail执行的功能.如果您的服务器使用400返回自定义验证消息,您可以从中xhr.responseText有效地覆盖默认you suck because your input was wrong fix it or else消息.
| 归档时间: |
|
| 查看次数: |
6907 次 |
| 最近记录: |