jquery验证不等待远程验证返回true,认为表单有效

时间:2022-12-01 08:25:58
$("#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.

即使值无效,此输出也为true。我看到验证最终失败并显示错误消息,但表单仍然提交。

6 个解决方案

#1


24  

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.

从jQuery Validate 1.11.1开始(甚至可能更旧),接受的答案不起作用。此外,这个问题没有简单的答案,解决方案需要为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().

实际上,简单的答案可能只是:如果您想要做的只是提交表单,请不要手动调用valid()。只需让Validate插件为您完成。在内部,它将等待所有异步请求完成,然后才允许提交表单。只有在手动检查valid()或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.

但是,有很多原因可能导致您需要这样做。例如,我正在处理的页面需要在启用表单的其余部分之前使用远程验证器检查字段的有效性。我可以手工完成,而不是使用jQuery Validation,但这是一个重复的工作。

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.

那么,为什么设置async:false不起作用?如果将async设置为false,则会同步进行请求,但插件不能正确处理此请求。内部远程函数总是返回“pending”,这将导致valid()函数返回true,即使请求已经完成并收到错误响应!它不会检查响应的值,也不会在以后显示错误。

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.

使用同步回调时使valid()和element()同步运行的解决方案是添加自定义验证方法。我自己试过这个,看起来效果很好。您可以从常规远程验证中复制源代码并修改它以处理同步aj​​ax调用,并且默认情况下是同步的。

The source code of of the remote function in v1.11.1 starts on line 1112 of jquery.validate.js:

v1.11.1中远程函数的源代码从jquery.validate.js的第1112行开始:

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.

注意即使ajax调用完成,它总是返回“pending”。

To fix this issue, make the following modifications:

要解决此问题,请进行以下修改:

  1. Move the declaration of the valid variable outside of the ajax call and the success function in order to make a closure, and assign it a default value of "pending".
  2. 将有效变量的声明移到ajax调用和success函数之外,以便进行闭包,并为其指定默认值“pending”。
  3. Change the old declaration of the valid variable to an assignment.
  4. 将有效变量的旧声明更改为赋值。
  5. Return the valid variable instead of the constant "pending".
  6. 返回有效变量而不是常量“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:

这是插件插件的完整代码。只需将其保存为js文件,并在包含jQuery验证后将其包含在页面或模板中:

//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.

我用自己的形式对此进行了测试,效果很好。在启用表单的其余部分之前,我可以测试元素的有效性。但是,您可能希望设置onkeyup:false以防止在每次按键时执行同步回调。我也喜欢使用onfocusout:false。

To use this, just replace "remote" in your validation settings with "synchronousRemote" everywhere you'd like to use this. For example:

要使用此功能,只需将验证设置中的“remote”替换为您想要使用它的“synchronousRemote”。例如:

$("#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


6  

Bumped into the same issue it seems that you have the set the remote call to synchronous - async: false

陷入同样的​​问题似乎你已经设置了对synchronous的远程调用--async:false

Otherwise the $("form").valid() will return true for the remote validation, please see below what I use

否则$(“form”)。valid()将返回true进行远程验证,请参阅下面我使用的内容

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


6  

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.

这利用了$(“#form”)。validate()。pendingRequest每当有远程验证时总是> 0的事实。

Note: this will not work with setting async: true in the remote request.

注意:这不适用于在远程请求中设置async:true。

#4


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:

修复此问题的最简单方法(在此处报告:https://github.com/jzaefferer/jquery-validation/issues/361)是检查表单是否有效两次:

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.

将远程规则设置为async:false并使用valid()检查有效性实际上等待调用完成,但不使用它们的返回值。再次调用valid()会将它们考虑在内。

A bit ugly but it works. I'm using jquery 2.1.0 and jquery-validation 1.11.1, BTW.

有点难看,但它的工作原理。我正在使用jquery 2.1.0和jquery-validation 1.11.1,BTW。

#5


0  

You have to stop the normal submit of the browser in case that the validation returns false.

如果验证返回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


0  

I know this is patch but I have fixed it by checking current ajax is pending

我知道这是补丁但我通过检查当前ajax挂起来修复它

I check current ajax call by $.active, It will give you 0 if not ajax is running

我通过$ .active来检查当前的ajax调用,如果不是ajax正在运行它会给你0

$('#frmControlFamily #Identifier').valid();            
if ($.active == 0) {
    // my save logic
}

#1


24  

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.

从jQuery Validate 1.11.1开始(甚至可能更旧),接受的答案不起作用。此外,这个问题没有简单的答案,解决方案需要为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().

实际上,简单的答案可能只是:如果您想要做的只是提交表单,请不要手动调用valid()。只需让Validate插件为您完成。在内部,它将等待所有异步请求完成,然后才允许提交表单。只有在手动检查valid()或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.

但是,有很多原因可能导致您需要这样做。例如,我正在处理的页面需要在启用表单的其余部分之前使用远程验证器检查字段的有效性。我可以手工完成,而不是使用jQuery Validation,但这是一个重复的工作。

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.

那么,为什么设置async:false不起作用?如果将async设置为false,则会同步进行请求,但插件不能正确处理此请求。内部远程函数总是返回“pending”,这将导致valid()函数返回true,即使请求已经完成并收到错误响应!它不会检查响应的值,也不会在以后显示错误。

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.

使用同步回调时使valid()和element()同步运行的解决方案是添加自定义验证方法。我自己试过这个,看起来效果很好。您可以从常规远程验证中复制源代码并修改它以处理同步aj​​ax调用,并且默认情况下是同步的。

The source code of of the remote function in v1.11.1 starts on line 1112 of jquery.validate.js:

v1.11.1中远程函数的源代码从jquery.validate.js的第1112行开始:

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.

注意即使ajax调用完成,它总是返回“pending”。

To fix this issue, make the following modifications:

要解决此问题,请进行以下修改:

  1. Move the declaration of the valid variable outside of the ajax call and the success function in order to make a closure, and assign it a default value of "pending".
  2. 将有效变量的声明移到ajax调用和success函数之外,以便进行闭包,并为其指定默认值“pending”。
  3. Change the old declaration of the valid variable to an assignment.
  4. 将有效变量的旧声明更改为赋值。
  5. Return the valid variable instead of the constant "pending".
  6. 返回有效变量而不是常量“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:

这是插件插件的完整代码。只需将其保存为js文件,并在包含jQuery验证后将其包含在页面或模板中:

//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.

我用自己的形式对此进行了测试,效果很好。在启用表单的其余部分之前,我可以测试元素的有效性。但是,您可能希望设置onkeyup:false以防止在每次按键时执行同步回调。我也喜欢使用onfocusout:false。

To use this, just replace "remote" in your validation settings with "synchronousRemote" everywhere you'd like to use this. For example:

要使用此功能,只需将验证设置中的“remote”替换为您想要使用它的“synchronousRemote”。例如:

$("#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


6  

Bumped into the same issue it seems that you have the set the remote call to synchronous - async: false

陷入同样的​​问题似乎你已经设置了对synchronous的远程调用--async:false

Otherwise the $("form").valid() will return true for the remote validation, please see below what I use

否则$(“form”)。valid()将返回true进行远程验证,请参阅下面我使用的内容

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


6  

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.

这利用了$(“#form”)。validate()。pendingRequest每当有远程验证时总是> 0的事实。

Note: this will not work with setting async: true in the remote request.

注意:这不适用于在远程请求中设置async:true。

#4


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:

修复此问题的最简单方法(在此处报告:https://github.com/jzaefferer/jquery-validation/issues/361)是检查表单是否有效两次:

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.

将远程规则设置为async:false并使用valid()检查有效性实际上等待调用完成,但不使用它们的返回值。再次调用valid()会将它们考虑在内。

A bit ugly but it works. I'm using jquery 2.1.0 and jquery-validation 1.11.1, BTW.

有点难看,但它的工作原理。我正在使用jquery 2.1.0和jquery-validation 1.11.1,BTW。

#5


0  

You have to stop the normal submit of the browser in case that the validation returns false.

如果验证返回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


0  

I know this is patch but I have fixed it by checking current ajax is pending

我知道这是补丁但我通过检查当前ajax挂起来修复它

I check current ajax call by $.active, It will give you 0 if not ajax is running

我通过$ .active来检查当前的ajax调用,如果不是ajax正在运行它会给你0

$('#frmControlFamily #Identifier').valid();            
if ($.active == 0) {
    // my save logic
}