问题
$("#new_component_form").validate({
errorClass: 'input-error',
rules : {
"comp_data[account_name]" : {
required: true,
remote: {
url: "/validate",
data: {
provider: 'twitter'
}
}
}
},
onsubmit: true,
onfocusout: false,
onkeyup: false,
onclick: false
});
$("#new_component_form").submit(function(){
console.log($(this).valid());
This outputs true, even if the value is invalid. I see the validation eventually fail and show the error message but the form is still submitted.
回答1:
As of jQuery Validate 1.11.1 (and perhaps even older), the accepted answer does not work. In addition, there is no easy answer to this question, and the solution requires adding a custom validation method to jQuery Validation.
Actually, the easy answer may just be: Don't call valid() manually if all you want to do is submit the form. Just let the Validate plugin do it for you. Internally, it will wait for all asynchronous requests to complete before allowing the form to be submitted. This issue only arises when you are manually checking valid() or element().
However, there are plenty of reasons why you may need to do that. For instance, the page I am working on needs to check the validity of a field using a remote validator before enabling the rest of the form. I could just do it by hand instead of using jQuery Validation, but that's a duplication of effort.
So, why does setting async: false not work? If you set async to false, the request will be made synchronously, however, the plugin doesn't handle this correctly. The internal remote function always returns "pending" which will cause the valid() function to return true even if the request is already complete and received a false response! It doesn't check the value of the response or show the error until later.
The solution to making valid() and element() behave synchronously when using a synchronous callback is to add a custom validation method. I've tried this myself, and it seems to work fine. You can just copy the source code from the regular remote validation and modify it to handle synchronous ajax calls, and be synchronous by default.
The source code of of the remote function in v1.11.1 starts on line 1112 of jquery.validate.js:
remote: function( value, element, param ) {
if ( this.optional(element) ) {
return "dependency-mismatch";
}
var previous = this.previousValue(element);
if (!this.settings.messages[element.name] ) {
this.settings.messages[element.name] = {};
}
previous.originalMessage = this.settings.messages[element.name].remote;
this.settings.messages[element.name].remote = previous.message;
param = typeof param === "string" && {url:param} || param;
if ( previous.old === value ) {
return previous.valid;
}
previous.old = value;
var validator = this;
this.startRequest(element);
var data = {};
data[element.name] = value;
$.ajax($.extend(true, {
url: param,
mode: "abort",
port: "validate" + element.name,
dataType: "json",
data: data,
success: function( response ) {
validator.settings.messages[element.name].remote = previous.originalMessage;
var valid = response === true || response === "true";
if ( valid ) {
var submitted = validator.formSubmitted;
validator.prepareElement(element);
validator.formSubmitted = submitted;
validator.successList.push(element);
delete validator.invalid[element.name];
validator.showErrors();
} else {
var errors = {};
var message = response || validator.defaultMessage( element, "remote" );
errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
validator.invalid[element.name] = true;
validator.showErrors(errors);
}
previous.valid = valid;
validator.stopRequest(element, valid);
}
}, param));
return "pending";
}
Notice how it always returns "pending" even if the ajax call is complete.
To fix this issue, make the following modifications:
- Move the declaration of the
validvariable outside of the ajax call and the success function in order to make a closure, and assign it a default value of "pending". - Change the old declaration of the
validvariable to an assignment. - Return the
validvariable instead of the constant "pending".
Here's the complete code for a plugin to the plugin. Just save this as a js file and include it in your page or template after the include for jQuery Validation:
//Created for jQuery Validation 1.11.1
$.validator.addMethod("synchronousRemote", function (value, element, param) {
if (this.optional(element)) {
return "dependency-mismatch";
}
var previous = this.previousValue(element);
if (!this.settings.messages[element.name]) {
this.settings.messages[element.name] = {};
}
previous.originalMessage = this.settings.messages[element.name].remote;
this.settings.messages[element.name].remote = previous.message;
param = typeof param === "string" && { url: param } || param;
if (previous.old === value) {
return previous.valid;
}
previous.old = value;
var validator = this;
this.startRequest(element);
var data = {};
data[element.name] = value;
var valid = "pending";
$.ajax($.extend(true, {
url: param,
async: false,
mode: "abort",
port: "validate" + element.name,
dataType: "json",
data: data,
success: function (response) {
validator.settings.messages[element.name].remote = previous.originalMessage;
valid = response === true || response === "true";
if (valid) {
var submitted = validator.formSubmitted;
validator.prepareElement(element);
validator.formSubmitted = submitted;
validator.successList.push(element);
delete validator.invalid[element.name];
validator.showErrors();
} else {
var errors = {};
var message = response || validator.defaultMessage(element, "remote");
errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
validator.invalid[element.name] = true;
validator.showErrors(errors);
}
previous.valid = valid;
validator.stopRequest(element, valid);
}
}, param));
return valid;
}, "Please fix this field.");
I've tested this with my own form and it works great. I can test my element for validity before enabling the rest of the form. However, you probably want to set onkeyup: false to prevent performing a synchronous callback on every key press. I also like to use onfocusout: false.
To use this, just replace "remote" in your validation settings with "synchronousRemote" everywhere you'd like to use this. For example:
$("#someForm").validate({
rules: {
someField: {
required: true,
synchronousRemote: {
url: "/SomePath/ValidateSomeField"
//notice that async: false need not be specified. It's the default.
}
}
},
messages: {
someField: {
required: "SomeField is required.",
synchronousRemote: "SomeField does not exist."
}
},
onkeyup: false,
onfocusout: false
});
回答2:
Bumped into the same issue it seems that you have the set the remote call to synchronous - async: false
Otherwise the $("form").valid() will return true for the remote validation, please see below what I use
rules: {
NameToValidate: {
required: true,
remote: function()
{
return {
type: "POST",
async: false,
url: "www.mysite.com/JSONStuff",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify( {
Name: "UNIQUE NAME"
} )
}
}
},
.....
回答3:
The easiest way to fix this problem (it's reported here: https://github.com/jzaefferer/jquery-validation/issues/361) is to check twice if the form is valid:
if (myForm.valid() && myForm.valid()) {
...
}
Setting the remote rules to async:false and checking for validity using valid() actually waits for the calls to finish, but their return values are not used. Calling valid() again takes them into account.
A bit ugly but it works. I'm using jquery 2.1.0 and jquery-validation 1.11.1, BTW.
回答4:
Here's a solution that we came up with on my project.
var validPendingTimeout;
function doSomethingWithValid() {
var isValid = $("#form").valid();
var isPending = $("#form").validate().pendingRequest !== 0;
if (isPending) {
if (typeof validPendingTimeout !== "undefined") {
clearTimeout(validPendingTimeout);
}
validPendingTimeout = setTimeout(doSomethingWithValid, 200);
}
if (isValid && !isPending) {
// do something when valid and not pending
} else {
// do something else when not valid or pending
}
}
This takes advantage of the fact that $("#form").validate().pendingRequest will always be > 0 whenever there is a remote validation.
Note: this will not work with setting async: true in the remote request.
回答5:
You have to stop the normal submit of the browser in case that the validation returns false.
$("#new_component_form").submit(function() {
if ($(this).valid())) {
console.log("I'm a valid form!");
} else {
console.log("I'm NOT a valid form!");
//Stop the normal submit of the browser
return false;
}
}
回答6:
I know this is patch but I have fixed it by checking current ajax is pending
I check current ajax call by $.active, It will give you 0 if not ajax is running
$('#frmControlFamily #Identifier').valid();
if ($.active == 0) {
// my save logic
}
回答7:
You could validate the form twice or check for the current ajax call to finish.
if (myForm.valid() && myForm.valid()) {
}
or
if ($.active == 0) {
}
So far the best solution i found was mentioned in the top of this thread. https://stackoverflow.com/a/20750164/11064080
But there is no proper solution. If you use two remote validation function in the same form all the above approaches will fail.
回答8:
I solved this by creating a custom function that checks both $("#form").valid() and $("#form").validate().pendingRequest:
function formIsValid() {
return $("#form").valid() && $("#form").validate().pendingRequest === 0;
}
This works on the assumption that $("#form").validate().pendingRequest will always be > 0 during a remote validation request. This works using jquery-validation 1.19.1.
来源:https://stackoverflow.com/questions/27287284/let-synchonousremote-show-custom-error-message-from-server