it-swarm.com.ru

jQuery Validate - требуется заполнить хотя бы одно поле в группе

Я использую отличный 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.

98
Nathan Long

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

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

Код внутри файла Additional-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!

21
Gerasimos

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

if(!$(element).data('being_validated')) {
  var fields = $(selector, element.form);
  fields.data('being_validated', true); 
  $(element.form).valid();
  fields.data('being_validated', false);
}
6
sean

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

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

положить в форму проверки сценария

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
Walter Kelly

Я отправил патч , который не страдает от проблем, возникающих в текущей версии (из-за чего опция "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
Kingsley

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

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

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

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

3
Michael Gundlach

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

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

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

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

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

2
Andrew Roazen

Вот мой ответ на ответ 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
squarecandy

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

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."));
1
Rocket Hazmat

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

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

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

0
Rinat