Начиная с jQuery Validate 1.11.1 (и, возможно, даже старше) принятый ответ не работает. Кроме того, нет простого ответа на этот вопрос, и решение требует добавления пользовательского метода проверки в jQuery Validation.
На самом деле, простой ответ может быть следующим: не звоните valid()
вручную, если все, что вы хотите сделать, это отправить форму. Просто позвольте плагину Validate сделать это за вас. Внутри он будет ожидать завершения всех асинхронных запросов, прежде чем разрешить отправку формы. Эта проблема возникает только при ручной проверке valid()
или element()
.
Однако существует множество причин, по которым вам может понадобиться это сделать. Например, страница, над которой я работаю, должна проверить правильность поля с помощью удаленного валидатора, прежде чем активировать оставшуюся часть формы. Я мог бы просто сделать это вручную вместо использования JQuery Validation, но это дублирование усилий.
Итак, почему настройка async: false
не работает? Если для async установлено значение false, запрос будет выполняться синхронно, однако плагин не будет правильно обрабатывать это. Внутренняя функция remote
всегда возвращает "pending"
, в результате чего функция valid()
возвращает true
, даже если запрос уже выполнен и получил ложный ответ! Он не проверяет значение ответа или покажите ошибку позже.
Решением заставить valid()
и element()
вести себя синхронно при использовании синхронного обратного вызова является добавление пользовательского метода проверки. Я попробовал это сам, и, кажется, работает нормально. Вы можете просто скопировать исходный код из обычной удаленной проверки и изменить его для обработки синхронных вызовов ajax и быть синхронным по умолчанию.
Исходный код удаленной функции в v1.11.1 начинается в строке 1112 файла 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";
}
Обратите внимание, что он всегда возвращает «в ожидании», даже если вызов ajax завершен.
Чтобы исправить эту проблему, внесите следующие изменения:
- Переместите объявление переменной
valid
за пределы вызова ajax и функции success, чтобы сделать замыкание, и присвойте ему значение по умолчанию «pending».
- Заменить старое объявление переменной
valid
на присвоение.
- Возвращает переменную
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
});