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.errorX зображення як його фону, якщо він не має класу .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.


Також дивіться тісно пов’язане правило - "Або пропустіть ці поля, або заповніть принаймні X з них" - stackoverflow.com/questions/1888976/…
Натан Лонг

Чому один довільний ввід несе відповідальність за перевірку, чи заповнені інші введення? Це не має сенсу. Можливо, ви могли б включити трохи розмітки із залученими елементами?
Монреаліст

@dalbaeb - я трохи уточнив приклад. Справа не в тому, що один довільний ввід відповідає за перевірку інших; це те, що кожен вхід у групу відповідає за перевірку всіх інших.
Натан Лонг

Це я подумав, дуже дякую!
монтреаліст

3
Дякую, це працює для мене, але інші необхідні поля у формі тепер більше не відповідають, якщо вони не отримають і втратять фокус після перевірки. (Хтось додав це як відповідь на ваше інше запитання, але його потрібно було позначити, оскільки це не відповідь).
mydoghasworms

Відповіді:


21

Це відмінне рішення Натан. Дуже дякую.

Ось спосіб, як змусити вищезазначений код працювати, якщо у когось виникнуть проблеми з його інтеграцією, як я:

Код всередині файла 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!


Радий, що вам це корисно, і дякую за чіпінг інформації. Однак замість того, щоб робити метод addClassRules, я вважаю за краще використовувати масив правил для кожної окремої форми. Якщо ви перейдете на цю сторінку ( jquery.bassistance.de/validate/demo/milk ) і натисніть "показати скрипт, який використовується на цій сторінці", ви побачите приклад. Я роблю це на крок далі: оголошую масив під назвою "правила", потім окремо я використовую їх з валідатором var = $ ('# formtovalidate'). Validate (правила);
Натан Лонг,

Ще одна думка: клас "філлон", який ви тут показуєте, може бути проблематичним. Що робити, якщо на тій же формі вам потрібно буде мати хоча б один номер деталі І хоча б одне ім’я контакту? Ваше правило дозволить 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. Тоді в методі вимагати від групи я перевіряю наявність цього прапора; якщо це там я роблю $(element.form).valid();, інакше роблю fields.valid();.
Крістоф

Хтось може пояснити, що насправді відбувається тут? У мене є досить подібне правило, яке працювало, але в якому ми не вирішили проблему переоцінки (інші поля в групі досі позначені як недійсні). Але зараз я відчуваю, що форма подається, навіть якщо вона недійсна. Якщо згруповані поля є дійсними, вони не вводять подаючого обробника, а якщо недійсні, - недійсні, але подають все-таки! Я б сказав, що це досить серйозна помилка в плагіні перевірки? Якщо правило, яке повертається, дійсне, стосується лише цього правила (навіть не всього поля), тож чому подається недійсна форма?
Адам

Я дослідив далі, і поля перед групою не підтверджуються швидко. Я запитав це як окреме питання (з частковим обхідним я discoivered): stackoverflow.com/questions/12690898 / ...
Adam

4

Спасибі Шон. Це вирішило проблему, яку я мав із кодом, ігноруючи інші правила.

Я також вніс декілька змін, щоб повідомлення "Будь ласка, заповніть принаймні 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>

додати до методу Requ_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."));

3

Починати ім'я змінної з $ потрібно в PHP, але досить дивно (IMHO) у Javascript. Також я вважаю, що ви посилаєтесь на це як "$ module" двічі та "module" один раз, правда? Здається, цей код не повинен працювати.

Крім того, я не впевнений, що це нормальний синтаксис плагінів jQuery, але я можу додати коментарі над вашим викликом addMethod, пояснюючи, що ви досягаєте. Навіть описуючи текст, описаний вище, важко дотримуватися коду, тому що я не знайомий з тим, на яке поле посилань: заповнене, значення, елемент чи селектор посилаються. Можливо, більшість цього очевидна для того, хто знайомий з плагіном Validate, тому використовуйте судження про те, що є правильним поясненням.

Можливо, у вас може вийти кілька варіантів для самостійного документування коду; подібно до,

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

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

Я точно не впевнений, що означає "модуль" насправді - чи є конкретніша назва, яку ви могли б дати цій змінній?

Гарний код, загалом!


Дякую за поради - я уточнив імена змінних і розбив код, щоб бути трохи читабельнішим.
Натан Лонг

2

Оскільки форма, над якою я працюю, має кілька клонованих регіонів із згрупованими вхідними даними, такі як я, я передав додатковий аргумент конструктору Requ_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 року.
Sparky

У мене є те саме питання, що у Rocket Hazmat з методом, який зараз постачається з валідатором. Він підтверджує, що одна група полів, але ніякі інші поля, що використовують інші методи, перевірені. Ця відповідь є спробою вирішити це питання. Якщо у вас є краще рішення, будь ласка, повідомте мене про це.
squarecandy

Поки розробник остаточно не виправить проблему, а не додає будь-якої плутанини, я просто рекомендую будь-яке тимчасове рішення тут затверджувати: github.com/jzaefferer/jquery-validation/isissue/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."));

Працює знов, щоб інші поля повторно перевірялися, але тепер, коли всі поля групи позначені як недійсні, і ви заповнюєте одне, лише те перевіряється. Принаймні для мене?
Крістоф

@Chris - дивіться мою нову відповідь, яка спирається на цю, і вирішує цю проблему.
squarecandy

0

Дякую, Натане Ти врятував мені тонну часу.

Однак я повинен зауважити, що це правило не готове до jQuery.noConflict (). Отже, треба замінити всі $ на jQuery, щоб працювати, скажімо,var $j = jQuery.noConflict()

І у мене виникає питання: як я можу змусити його вести себе як вбудоване правило? Наприклад, якщо я ввожу електронну пошту, повідомлення "Будь ласка, введіть дійсну електронну пошту" автоматично зникає, але якщо я заповнюю одне з групових полів, повідомлення про помилку залишається.


Ви маєте рацію - я не вважав ситуацію безконфліктною. Я можу це оновити в майбутньому, але ви можете легко знайти, або замінити, якщо хочете. Що стосується вашого другого питання, я не бачу тієї самої проблеми. Швидкий тест, якщо потрібно одне поле з групи, як тільки я щось набираю, ціла група передає це правило. Якщо потрібно більше одного, як тільки заповнюється останній і втрачає фокус, проходить вся група. Це те, що ти бачиш?
Натан Лонг

Хм, чомусь накручена націнка, і я не мав успіху в її виправленні
Рінат

Рінат - чи можете ви спростити та звузити проблему? Спробуйте використовувати мій код у більш простій формі, яка не потребує безконфліктних змін. Складіть найпростішу форму, на яку ви можете її протестувати, і спершу працюйте.
Натан Лонг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.