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

发布于 2024-12-02 09:52:50 字数 485 浏览 6 评论 0原文

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

小耗子 2024-12-09 09:52:50

从 jQuery Validate 1.11.1(甚至更旧)开始,接受的答案不起作用。另外,这个问题没有简单的答案,解决方案需要在 jQuery Validation 中添加自定义验证方法。

实际上,简单的答案可能就是:如果您只想提交表单,请不要手动调用 valid()。只需让验证插件为您完成即可。在内部,它将等待所有异步请求完成,然后才允许提交表单。仅当您手动检查 valid()element() 时,才会出现此问题。

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

那么,为什么设置async: false不起作用呢?如果将 async 设置为 false,请求将同步发出,但是插件无法正确处理此问题。内部remote函数始终返回“pending”,这将导致valid()函数返回true即使请求已经完成并收到错误响应!它直到稍后才会检查响应的值或显示错误。

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

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";
}

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

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

  1. valid 变量的声明移到 ajax 调用和 success 函数之外以创建闭包,并为其分配默认值“pending” 。
  2. valid 变量的旧声明更改为赋值。
  3. 返回 valid 变量而不是常量“pending”。

这是插件的完整代码。只需将其保存为 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.");

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

要使用此功能,只需将验证设置中的“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
});

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:

  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. Change the old declaration of the valid variable to an assignment.
  3. Return the valid variable 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
});
晨与橙与城 2024-12-09 09:52:50

遇到同样的问题,似乎您已将远程调用设置为同步 - async: false

否则 $("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"
            } )
        }
    }
},
.....

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"
            } )
        }
    }
},
.....
最单纯的乌龟 2024-12-09 09:52:50

这是我们在我的项目中提出的解决方案。

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
  }
}

这利用了 $("#form").validate().pendingRequest 将始终 > 的事实。 0 每当有远程验证时。

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

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.

逆流 2024-12-09 09:52:50

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

if (myForm.valid() && myForm.valid()) {
    ...
}

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

有点丑,但它有效。我正在使用 jquery 2.1.0 和 jquery-validation 1.11.1,顺便说一句。

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.

给妤﹃绝世温柔 2024-12-09 09:52:50

我通过创建一个检查 $("#form").valid()$("#form").validate().pendingRequest 的自定义函数解决了这个问题:

function formIsValid() {
    return $("#form").valid() && $("#form").validate().pendingRequest === 0;
}

这基于 $("#form").validate().pendingRequest 始终 > 的假设。 0 在远程验证请求期间。这可以使用 jquery-validation 1.19.1 进行。

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.

风吹雪碎 2024-12-09 09:52:50

您可以验证表单两次或检查当前的 ajax 调用是否完成。

if (myForm.valid() && myForm.valid()) {

}

或者

if ($.active == 0) {

}

到目前为止,我发现的最佳解决方案已在该线程的顶部提到。 https://stackoverflow.com/a/20750164/11064080

但没有适当的解决方案。如果您以相同的形式使用两个远程验证函数,则上述所有方法都将失败。

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.

踏月而来 2024-12-09 09:52:50

您必须停止浏览器的正常提交,以防验证返回 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;
    }
}

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;
    }
}
最后的乘客 2024-12-09 09:52:50

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

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

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

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
}
够钟 2024-12-09 09:52:50

2022 答案:这是针对 Kendo 的,但我不明白为什么你不能将其转换为 jQuery。它检查 ajax 是否在某个时间间隔内处于活动状态(将在远程验证期间)。

async function asyncValidate() {
    const isAjaxActive = () => $.active > 0;
    validator.validate();
    return new Promise(resolve => {
        var interval = setInterval(() => {
            if (!isAjaxActive()) {
                clearInterval(interval);
                resolve(validator.errors()?.length == 0 ?? true);
            }
        }, 25);
    });
}

if (!await asyncValidate()) {
    // failed validation
}

2022 answer: This is for Kendo but I don't see why you couldn't convert it to jQuery. It checks if ajax is active on an interval (it will be during remote validation).

async function asyncValidate() {
    const isAjaxActive = () => $.active > 0;
    validator.validate();
    return new Promise(resolve => {
        var interval = setInterval(() => {
            if (!isAjaxActive()) {
                clearInterval(interval);
                resolve(validator.errors()?.length == 0 ?? true);
            }
        }, 25);
    });
}

if (!await asyncValidate()) {
    // failed validation
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文