Проверка jQuery - требуется заполнить хотя бы одно поле в группе - PullRequest
98 голосов
/ 19 августа 2009

Я использую отличный jQuery Validate Plugin для проверки некоторых форм. В одной форме мне нужно убедиться, что пользователь заполняет хотя бы одно из группы полей. Я думаю, что у меня есть довольно хорошее решение, и я хотел бы поделиться им. Пожалуйста, предложите любые улучшения, которые вы можете придумать.

Не найдя встроенного способа сделать это, я искал и нашел Пользовательский метод проверки Ребекки Мерфи , который был очень полезен.

Я улучшил это тремя способами:

  1. Чтобы позволить вам передать селектор для группы полей
  2. Чтобы указать, сколько из этой группы должно быть заполнено для прохождения проверки
  3. Показать все входы в группе как проходящие проверку, как только один из них пройдет Проверка. (См. Крик Нику Крейверу в конце.)

Таким образом, вы можете сказать "как минимум 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's see. I might be valid if YOU'RE valid. Are you?"
    // Element 1: "Let's 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's .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's 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."));

Ура!

Вопль

Теперь для этого крика - изначально мой код просто слепо скрывал сообщения об ошибках в других соответствующих полях вместо их повторной проверки, что означало, что если возникла другая проблема (например, «разрешены только цифры, и вы ввели» письма '), он стал скрытым, пока пользователь не попытался отправить. Это было потому, что я не знал, как избежать обратной связи, упомянутой в комментариях выше. Я знал, что должен быть способ, поэтому Я задал вопрос , и Ник Крейвер просветил меня. Спасибо, Ник!

Вопрос решен

Первоначально это был вопрос «позвольте мне поделиться этим и посмотреть, может ли кто-нибудь предложить улучшения». Хотя я все еще приветствую отзывы, я думаю, что на данный момент они довольно полные. (Это может быть короче, но я хочу, чтобы оно было легко читаемым и не обязательно кратким.) Так что просто наслаждайтесь!

Обновление - теперь часть проверки jQuery

Это было официально добавлено в jQuery Validation 4/3/2012.

Ответы [ 9 ]

21 голосов
/ 27 сентября 2009

Это отличное решение, Натан. Большое спасибо.

Вот способ заставить приведенный выше код работать на тот случай, если у кого-то возникнут проблемы с его интеграцией, как я сделал:

Код внутри файла extra-method.js :

jQuery.validator.addMethod("require_from_group", function(value, element, options) {
...// Nathan's 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" />

Не забудьте включить файл Additional-method.js!

6 голосов
/ 02 мая 2011

Хорошее решение. Тем не менее, у меня была проблема других обязательных правил не работает. Выполнение .valid () для формы исправило эту проблему для меня.

if(!$(element).data('being_validated')) {
  var fields = $(selector, element.form);
  fields.data('being_validated', true); 
  $(element.form).valid();
  fields.data('being_validated', false);
}
4 голосов
/ 12 июля 2013

Я отправил патч , который не страдает от проблем, возникающих в текущей версии (в результате чего опция «required» перестает корректно работать в других полях, обсуждение проблем с текущей версией включен 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."));
4 голосов
/ 10 июня 2011

Спасибо, Шон. Это исправило проблему с кодом, игнорирующим другие правила.

Я также внес несколько изменений, чтобы сообщение «Пожалуйста, заполните хотя бы одно поле ...» показывалось в отдельном элементе вместо всех полей.

введите в форму сценарий проверки

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;(!)"));
3 голосов
/ 20 августа 2009

Начинать имя переменной с $ требуется в 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(...)

(при условии, что я понял значение этих фрагментов вашего кода! :))

Я не совсем уверен, что на самом деле означает «модуль» - есть ли более конкретное имя, которое вы могли бы дать этой переменной?

Хороший код, в целом!

2 голосов
/ 26 февраля 2013

Вот мой ответ на ответ 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."));

Единственная оставшаяся проблема с этим сейчас - это крайний случай, когда поле пусто, затем заполнено, затем снова пусто ... в этом случае ошибка будет применена к одному полю, а не к группе. Но это вряд ли произойдет с какой-либо частотой, и в этом случае технически это работает.

2 голосов
/ 02 марта 2010

Поскольку форма, над которой я работаю, имеет несколько клонированных областей с сгруппированными входными данными, подобными этим, я передал дополнительный аргумент конструктору require_from_group, изменив ровно одну строку вашей функции addMethod:

var commonParent = $(element).parents(options[2]);

и таким образом селектор, идентификатор или имя элемента могут быть переданы один раз:

jQuery.validator.addClassRules("reqgrp", {require_from_group: [1, ".reqgrp", 'fieldset']});

и валидатор ограничит валидацию элементами с этим классом только внутри каждого набора полей, вместо того, чтобы пытаться подсчитать все классифицированные элементы .reqgrp в форме.

1 голос
/ 09 февраля 2012

У меня были проблемы с другими правилами, которые не проверялись в связи с этим, поэтому я изменил:

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."));
0 голосов
/ 12 мая 2010

Спасибо, Натан. Вы сэкономили мне кучу времени.

Однако я должен заметить, что это правило не готово к jQuery.noConflict (). Таким образом, для работы с, скажем, var $j = jQuery.noConflict()

необходимо заменить все $ на jQuery.

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

...