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(" &nbsp;(!)"));

Ответ 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()

И у меня вопрос: как заставить его вести себя как встроенное правило? Например, если я вхожу в адрес электронной почты, сообщение "Пожалуйста, введите действительный адрес электронной почты" автоматически исчезнет, ​​но если я заполнил одно из сообщений об ошибках группового поля,