JQuery Validate - требует, по крайней мере, одного поля в заполняемой группе
Я использую отличный jQuery Validate Plugin для проверки некоторых форм. В одной форме мне нужно убедиться, что пользователь заполняет хотя бы одну из группы полей. Я думаю, что у меня неплохое решение, и я хотел бы поделиться им. Просьба предложить любые улучшения, о которых вы можете подумать.
Не найдя встроенного способа сделать это, я искал и нашел метод выборочной проверки Ребекки Мерфи, что было очень полезно.
Я улучшил это тремя способами:
- Чтобы передать вам селектор для группы полей
- Чтобы указать, сколько из этой группы должно быть заполнено для проверки, чтобы пройти
- Чтобы показать все входы в группе как прохождение проверки, как только один из них пройдет
Проверка. (См. Крик-к Nick Craver в конце.)
Итак, вы можете сказать "по крайней мере X входов, которые соответствуют селектору Y, должны быть заполнены."
Конечный результат: с разметкой:
<input class="productinfo" name="partnumber">
<input class="productinfo" name="description">
... представляет собой группу правил, подобных этому:
// Both these inputs input will validate if
// at least 1 input with class 'productinfo' is filled
partnumber: {
require_from_group: [1,".productinfo"]
}
description: {
require_from_group: [1,".productinfo"]
}
Пункт №3 предполагает, что вы добавляете класс .checked
к сообщениям об ошибках после успешной проверки. Вы можете сделать это следующим образом: как показано здесь.
success: function(label) {
label.html(" ").addClass("checked");
}
Как и в приведенной выше демонстрационной версии, я использую CSS, чтобы каждый span.error
отображал изображение X в качестве фона, если только он не имеет класс .checked
, и в этом случае он получает изображение галочки.
Здесь мой код:
jQuery.validator.addMethod("require_from_group", function(value, element, options) {
var numberRequired = options[0];
var selector = options[1];
//Look for our selector within the parent form
var validOrNot = $(selector, element.form).filter(function() {
// Each field is kept if it has a value
return $(this).val();
// Set to true if there are enough, else to false
}).length >= numberRequired;
// The elegent part - this element needs to check the others that match the
// selector, but we don't want to set off a feedback loop where each element
// has to check each other element. It would be like:
// Element 1: "I might be valid if you're valid. Are you?"
// Element 2: "Let see. I might be valid if YOU'RE valid. Are you?"
// Element 1: "Let see. I might be valid if YOU'RE valid. Are you?"
// ...etc, until we get a "too much recursion" error.
//
// So instead we
// 1) Flag all matching elements as 'currently being validated'
// using jQuery .data()
// 2) Re-run validation on each of them. Since the others are now
// flagged as being in the process, they will skip this section,
// and therefore won't turn around and validate everything else
// 3) Once that done, we remove the 'currently being validated' flag
// from all the elements
if(!$(element).data('being_validated')) {
var fields = $(selector, element.form);
fields.data('being_validated', true);
// .valid() means "validate using all applicable rules" (which
// includes this one)
fields.valid();
fields.data('being_validated', false);
}
return validOrNot;
// {0} below is the 0th item in the options field
}, jQuery.format("Please fill out at least {0} of these fields."));
Ура!
Кричать
Теперь для этого крика - изначально мой код просто слепо скрывал сообщения об ошибках в других совпадающих полях вместо повторной проверки их, что означало, что если возникла другая проблема (например, "только номера разрешены и вы ввели буквы '), он был скрыт, пока пользователь не попытался отправить. Это произошло потому, что я не знал, как избежать цикла обратной связи, упомянутого в комментариях выше. Я знал, что должен быть способ, поэтому я задал вопрос, и Nick Craver просветил меня. Спасибо, Ник!
Вопрос Решенный
Первоначально это было "позвольте мне рассказать об этом и посмотреть, может ли кто-нибудь предложить предложения". Хотя я по-прежнему приветствую обратную связь, я думаю, что это довольно полно. (Это может быть короче, но я хочу, чтобы это было легко читать и не обязательно кратким.) Так что просто наслаждайтесь!
Обновление - теперь часть проверки jQuery
Это было официально добавлено в jQuery Validation 4/3/2012.
Ответы
Ответ 1
Это отличное решение Натана. Большое спасибо.
Здесь способ, которым работает вышеприведенный код, на случай, если кто-то столкнется с проблемой интеграции с ним, как и я:
Код внутри файла extra-methods.js:
jQuery.validator.addMethod("require_from_group", function(value, element, options) {
...// Nathan code without any changes
}, jQuery.format("Please fill out at least {0} of these fields."));
// "filone" is the class we will use for the input elements at this example
jQuery.validator.addClassRules("fillone", {
require_from_group: [1,".fillone"]
});
Код внутри файла html:
<input id="field1" class="fillone" type="text" value="" name="field1" />
<input id="field2" class="fillone" type="text" value="" name="field2" />
<input id="field3" class="fillone" type="text" value="" name="field3" />
<input id="field4" class="fillone" type="text" value="" name="field4" />
Не забудьте включить файл дополнительных-методов .js!
Ответ 2
Хорошее решение. Однако у меня была проблема с другими необходимыми правилами, которые не работают. Выполнение .valid() в отношении формы устранило эту проблему для меня.
if(!$(element).data('being_validated')) {
var fields = $(selector, element.form);
fields.data('being_validated', true);
$(element.form).valid();
fields.data('being_validated', false);
}
Ответ 3
Спасибо, Sean. Это исправило проблему, с которой я столкнулся с кодом, игнорирующим другие правила.
Я также сделал несколько изменений, чтобы сообщение "Пожалуйста, заполните не менее 1 поля.." в отдельном div вместо каждого поля.
положить в форму validate script
showErrors: function(errorMap, errorList){
$("#form_error").html("Please fill out at least 1 field before submitting.");
this.defaultShowErrors();
},
добавить это где-нибудь на странице
<div class="error" id="form_error"></div>
добавить в метод require_from_group метод addMethod
if(validOrNot){
$("#form_error").hide();
}else{
$("#form_error").show();
}
......
}, jQuery.format(" (!)"));
Ответ 4
Я отправил патч, который не страдает от проблем, возникающих в текущей версии (в силу чего требуется "требуется" "опция перестает нормально работать в других областях, обсуждение проблем с текущей версией происходит на github.
Пример http://jsfiddle.net/f887W/10/
jQuery.validator.addMethod("require_from_group", function (value, element, options) {
var validator = this;
var minRequired = options[0];
var selector = options[1];
var validOrNot = jQuery(selector, element.form).filter(function () {
return validator.elementValue(this);
}).length >= minRequired;
// remove all events in namespace require_from_group
jQuery(selector, element.form).off('.require_from_group');
//add the required events to trigger revalidation if setting is enabled in the validator
if (this.settings.onkeyup) {
jQuery(selector, element.form).on({
'keyup.require_from_group': function (e) {
jQuery(selector, element.form).valid();
}
});
}
if (this.settings.onfocusin) {
jQuery(selector, element.form).on({
'focusin.require_from_group': function (e) {
jQuery(selector, element.form).valid();
}
});
}
if (this.settings.click) {
jQuery(selector, element.form).on({
'click.require_from_group': function (e) {
jQuery(selector, element.form).valid();
}
});
}
if (this.settings.onfocusout) {
jQuery(selector, element.form).on({
'focusout.require_from_group': function (e) {
jQuery(selector, element.form).valid();
}
});
}
return validOrNot;
}, jQuery.format("Please fill at least {0} of these fields."));
Ответ 5
Запуск имени переменной с $требуется в PHP, но довольно странно (IMHO) в Javascript. Кроме того, я считаю, что вы ссылаетесь на него как "$ module" дважды и "module" один раз, правильно? Кажется, что этот код не должен работать.
Кроме того, я не уверен, что это обычный синтаксис плагина jQuery, но я могу добавить комментарии над вашим вызовом addMethod, объясняя, что вы делаете. Даже с вашим текстовым описанием выше, трудно следовать коду, потому что я не знаю, к чему относятся fieldset,: fill, value, element или selector. Возможно, большая часть этого очевидна для кого-то, знакомого с плагином Validate, поэтому используйте суждение о том, что представляет собой правильное количество объяснений.
Возможно, вы могли бы вырвать несколько варов для самодокументации кода; как,
var atLeastOneFilled = module.find(...).length > 0;
if (atLeastOneFilled) {
var stillMarkedWithErrors = module.find(...).next(...).not(...);
stillMarkedWithErrors.text("").addClass(...)
(предполагая, что я понял смысл этих кусков вашего кода!:))
Я не совсем уверен, что означает "модуль", на самом деле - есть ли более конкретное имя, которое вы могли бы дать этой переменной?
Хороший код, в целом!
Ответ 6
Поскольку у формы, над которой я работаю, есть несколько клонированных областей с такими сгруппированными входами, я передал дополнительный аргумент конструктору require_from_group, изменяя ровно одну строку вашей функции addMethod:
var commonParent = $(element).parents(options[2]);
и таким образом можно выбрать один из селекторов, идентификаторов или имени элемента:
jQuery.validator.addClassRules("reqgrp", {require_from_group: [1, ".reqgrp", 'fieldset']});
и валидатор ограничивает проверку элементов этим классом только внутри каждого набора полей, а не пытается подсчитать все классические элементы .reqgrp в форме.
Ответ 7
Здесь моя трещина в Rocket Hazmat отвечает, пытаясь решить проблему с другими определенными полями, также нуждающуюся в проверке, но отмечая все поля как действительные при успешном заполнении одного.
jQuery.validator.addMethod("require_from_group", function(value, element, options){
var numberRequired = options[0],
selector = options[1],
$fields = $(selector, element.form),
validOrNot = $fields.filter(function() {
return $(this).val();
}).length >= numberRequired,
validator = this;
if(!$(element).data('being_validated')) {
$fields.data('being_validated', true).each(function(){
validator.valid(this);
}).data('being_validated', false);
}
if (validOrNot) {
$(selector).each(function() {
$(this).removeClass('error');
$('label.error[for='+$(this).attr('id')+']').remove();
});
}
return validOrNot;
}, jQuery.format("Please fill out at least {0} of these fields."));
Единственная оставшаяся проблема с этим сейчас - это крайний случай, когда поле пустое, затем заполнено, а затем снова пусто... В этом случае ошибка будет применяться к одному полю, а не к группе. Но это кажется настолько маловероятным с любой частотой, и в этом случае оно технически работает.
Ответ 8
У меня были проблемы с другими правилами, которые не проверялись в связи с этим, поэтому я изменил:
fields.valid();
Для этого:
var validator = this;
fields.each(function(){
validator.valid(this);
});
Я также сделал несколько (личных) улучшений, и это версия, которую я использую:
jQuery.validator.addMethod("require_from_group", function(value, element, options){
var numberRequired = options[0],
selector = options[1],
$fields = $(selector, element.form),
validOrNot = $fields.filter(function() {
return $(this).val();
}).length >= numberRequired,
validator = this;
if(!$(element).data('being_validated')) {
$fields.data('being_validated', true).each(function(){
validator.valid(this);
}).data('being_validated', false);
}
return validOrNot;
}, jQuery.format("Please fill out at least {0} of these fields."));
Ответ 9
Спасибо, Натан. Ты спас мне много времени.
Однако я должен заметить, что это правило не является jQuery.noConflict() готово. Итак, нужно заменить все $на jQuery для работы, скажем, var $j = jQuery.noConflict()
И у меня вопрос: как заставить его вести себя как встроенное правило? Например, если я вхожу в адрес электронной почты, сообщение "Пожалуйста, введите действительный адрес электронной почты" автоматически исчезнет, но если я заполнил одно из сообщений об ошибках группового поля,