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

98

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

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

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

  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 3 апреля 2012 г.

Натан Лонг
источник
Также см. Тесно связанное правило - «Либо пропустите эти поля, либо заполните хотя бы X из них» - stackoverflow.com/questions/1888976/…
Натан Лонг,
Почему один произвольный вход должен отвечать за проверку того, заполнены ли другие входы? В этом нет смысла. Возможно, вы могли бы добавить немного разметки к задействованным элементам?
монреалист
@dalbaeb - Я немного пояснил пример. Дело не в том, что один произвольный ввод отвечает за проверку других; Дело в том, что каждый вход в группе отвечает за проверку всех остальных.
Натан Лонг
Вот что я подумал, большое спасибо!
montrealist
3
Спасибо, это работает для меня, но другие обязательные поля в форме теперь больше не отвечают, если они не приобретают или не теряют фокус после проверки. (Кто-то добавил это как ответ на ваш другой вопрос, но его нужно было пометить, потому что это не ответ).
mydoghasworms

Ответы:

21

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

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

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


источник
Рад, что это помогло вам, и спасибо за информацию. Однако вместо использования метода addClassRules я предпочитаю использовать массив правил для каждой отдельной формы. Если вы перейдете на эту страницу ( jquery.bassistance.de/validate/demo/milk ) и нажмете «показать скрипт, используемый на этой странице», вы увидите пример. Я делаю еще один шаг: я объявляю массив под названием «rules», затем отдельно использую их с var validator = $ ('# formtovalidate'). Validate (rules);
Натан Лонг,
Еще одна мысль: показанный здесь класс fillone может быть проблематичным. Что, если в той же форме вам нужно потребовать хотя бы один номер детали И хотя бы одно контактное имя? Ваше правило допускает 0 имен контактов, если есть хотя бы один номер детали. Я думаю, что лучше установить такие правила, как require_from_group: [1,".partnumber"]и ...[1,".contactname"]убедиться, что вы подтверждаете правильные вещи.
Натан Лонг,
6

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

if(!$(element).data('being_validated')) {
  var fields = $(selector, element.form);
  fields.data('being_validated', true); 
  $(element.form).valid();
  fields.data('being_validated', false);
}
Шон
источник
1
Спасибо, Шон, у меня тоже была эта проблема. Однако есть одна проблема с этим решением, когда пользователь впервые попадает в форму - как только он заполняет первое поле запроса от группы, все остальные поля формы будут проверены и, таким образом, помечены как ошибочные. Я решил эту проблему, добавив обработчик form.submit () перед созданием экземпляра валидатора, в котором я установил флаг validator.formSubmit = true. Затем в методе require-from-group я проверяю наличие этого флага; если он там есть $(element.form).valid();, то есть fields.valid();.
Christof
Может ли кто-нибудь объяснить, что на самом деле здесь происходит? У меня есть довольно похожее правило, которое сработало, но в котором мы не решили проблему повторной проверки (другие поля в группе все еще помечены как недопустимые). Но теперь я чувствую, что форма отправляется, даже если она недействительна. Если сгруппированные поля действительны, то он не входит в обработчик отправки, а если недопустим, он входит в invalidHandler, но все равно отправляет! Я бы сказал, что это довольно серьезная ошибка в плагине проверки? То, что правило возвращает действительный, применяется только к этому правилу (даже не ко всему полю), так почему отправляется недопустимая форма?
Адам
Я исследовал дальше, и это поля перед группой, которые не проверяют proeprly. Я задал это как отдельный вопрос (с частичным обходным путем, который я обнаружил): stackoverflow.com/questions/12690898/…
Адам
4

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

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

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

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."));
оборота docflabby
источник
3

Запуск имени переменной с $ требуется в PHP, но довольно странно (IMHO) в Javascript. Кроме того, я считаю, что вы называете его «$ модуль» дважды и «модуль» один раз, верно? Похоже, этот код работать не должен.

Кроме того, я не уверен, что это обычный синтаксис плагина jQuery, но я мог бы добавить комментарии над вашим вызовом addMethod, объясняя, что вы делаете. Даже с вашим текстовым описанием выше сложно следовать коду, потому что я не знаю, к какому набору полей,: заполненному, значению, элементу или селектору относятся. Возможно, большая часть этого очевидна для кого-то, кто знаком с плагином Validate, поэтому используйте суждение о том, какое количество объяснений является правильным.

Возможно, вы могли бы выделить несколько переменных, чтобы самостоятельно задокументировать код; лайк,

var atLeastOneFilled = module.find(...).length > 0;
if (atLeastOneFilled) {
  var stillMarkedWithErrors = module.find(...).next(...).not(...);
  stillMarkedWithErrors.text("").addClass(...)

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

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

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

Майкл Гундлах
источник
Спасибо за предложения - я уточнил имена переменных и разбил код, чтобы он был более читабельным.
Натан Лонг,
2

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

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

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

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

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

Эндрю Роазен
источник
2

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

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

квадратные конфеты
источник
В этом ответе нет смысла, так как этот метод / правило было интегрировано в плагин еще в апреле 2012 года.
Спарки,
У меня та же проблема, что и у Rocket Hazmat, с методом, который теперь поставляется с валидатором. Он проверяет, что одна группа полей, но никакие другие поля, использующие другие методы, не проверяются. Этот ответ - попытка решить эту проблему. Если у вас есть лучшее решение, дайте мне знать.
squarecandy 02
Пока разработчик не исправит проблему навсегда, вместо того, чтобы вносить путаницу, я просто рекомендую любое временное решение, одобренное здесь: github.com/jzaefferer/jquery-validation/issues/412
Sparky
1

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

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."));
Ракетный Хазмат
источник
Работает, заставляя другие поля проверяться снова, но теперь, когда все поля группы помечены как недопустимые и вы заполняете одно, проверяется только одно. По крайней мере для меня?
Christof
@Chris - посмотрите мой новый ответ, который основан на этом и посвящен этому.
squarecandy
0

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

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

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

Рината
источник
Вы правы - неконфликтную ситуацию я не учел. Я могу обновить это в будущем, но вы можете легко найти и заменить, если хотите. Что касается вашего второго вопроса, я не вижу такой проблемы. С помощью быстрой проверки, если требуется одно поле из группы, как только я что-то набираю, вся группа проходит это правило. Если требуется более одного, как только последний требуемый заполняется и теряет фокус, вся группа проходит. Это то, что вы видите?
Натан Лонг,
хм, разметка по какой-то причине напортачила, и исправить это не удалось
Ринат
Ринат - можно ли упростить и сузить задачу? Попробуйте использовать мой код в более простой форме, не требующей внесения неконфликтных изменений. Создайте простейшую форму, на которой вы можете ее протестировать, и сначала заставьте ее работать.
Натан Лонг,