我真的很喜欢Eric Barnard's knockout validation lib与 observable 集成,允许分组,并提供自定义验证器可插入性(包括即时验证器)。有几个地方可以使用户体验更加灵活/友好,但总体而言,它的文档相当齐全... 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);
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;
}
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__可观察到 true 并退出。 callBack函数是作为第三个参数传递给异步的 validator在 MyViewModel 中声明的函数.然而在这发生之前,一个 isValidating observable 的订阅者被调用来通知异步验证已经开始。当服务器调用完成时,回调被调用(在这种情况下只传递 true 或 false)。MyViewModel 中的断点的原因当服务器端验证失败时导致无限循环:在 callBack 中上面的函数,注意 __valid__当验证失败时,observable 设置为 false。这是发生的事情:nestedModel2.prop2可观察的。 ko.validation.validateObservable通过订阅通知此更改。 validateAsync函数被调用。 $.ajax调用服务器并退出。 ko.validation.validateObservable 设置 __valid__可观察到 true并退出 . callBack(false)被执行。 callBack功能集__valid__至 false . ko.validation.validateObservable通知 __valid__ 的更改observable ( callBack 将其从 true 更改为 false ) 这基本上重复了上面的步骤 2。 ko.validation.validateObservable订阅处理程序不仅监听用户输入值的更改,还监听其嵌套 __valid__ 的更改。可观察的。这是一个错误,还是我做错了什么?ko.validation看到上面的来源表明,当服务器验证它时,带有异步验证器的用户输入值被视为有效。为此,调用nestedModel2.isValid()不能依赖“真相”。相反,看起来我们必须使用 isValidating Hook 以创建对异步验证器的订阅,并且仅在通知值 false 后才做出这些决定.这是故意的吗?与库的其余部分相比,这似乎是最反直觉的,因为 非 异步验证器没有 isValidating订阅,和 可以 依赖 .isValid()说实话。这也是设计使然,还是我在这里也做错了什么?
最佳答案
所以我问的问题实际上与如何在 ko.validation 中使用异步验证器有关。我从我的经验中学到了两个重要的收获:
async Anonymous or Single-Use Custom Rule validators .相反,将它们创建为 Custom Rules .否则,您最终会遇到我的问题中描述的无限循环/ping 匹配。 async验证器,不要相信 isValid()直到所有 async验证器 isValidating subscriptions更改为假。 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() });
}
};
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 });
});
}
};
callback arg 到 validator告诉 ko.validation 验证是否成功的函数。该调用将触发 isValidating已验证属性 observables 上的 observables 改回 false (意思是,异步验证已经完成,现在知道输入是否有效)。.done要执行的函数,因为它等同于 $.ajax success .如果您的服务器在验证失败时返回 HTTP 400(错误请求)状态,则会触发 .fail要执行的功能。如果您的服务器返回带有 400 的自定义验证消息,您可以从 xhr.responseText 获取该消息。有效地覆盖默认值 you suck because your input was wrong fix it or else信息。
关于javascript - knockout 验证异步验证器 : Is this a bug or am I doing something wrong?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12721951/
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:
当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested
我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.