web-dev-qa-db-ja.com

jQuery Validate-グループ内の少なくとも1つのフィールドに入力する必要があります

優れたjQuery Validate Plugin を使用していくつかのフォームを検証しています。 1つのフォームで、ユーザーがフィールドグループの少なくとも1つに入力することを確認する必要があります。かなり良い解決策があり、それを共有したいと思いました。 考えられる改善点を提案してください。

これを行うための組み込みの方法が見つからないため、検索して見つけました Rebecca Murpheyのカスタム検証方法 、これは非常に役に立ちました。

これを次の3つの方法で改善しました。

  1. フィールドのグループのセレクターを渡すには
  2. 検証に合格するために入力する必要があるグループの数を指定するには
  3. グループのすべての入力が検証の1つに合格するとすぐに検証に合格として表示する。 (最後に Nick Craver へのコメントを参照してください。)

ですから、「セレクターYに一致する少なくともX個の入力を埋める必要があります」と言うことができます。

次のようなマークアップの最終結果:

<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"); 
}

上記のリンクのデモのように、クラスspan.errorを持たない限り、CSSを使用して各.checkedに背景としてXイメージを与えます。その場合、チェックマークイメージを取得します。

これまでの私のコードは次のとおりです。

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

やった!

大声で叫ぶ

今、その叫びのために-元々、私のコードは再検証するのではなく、他の一致するフィールドのエラーメッセージを盲目的に隠していました。つまり、別の問題があった場合(「数字のみが許可され、文字を入力した」など) 、ユーザーが送信を試みるまで非表示になりました。これは、上記のコメントで言及したフィードバックループを回避する方法がわからなかったためです。方法があるはずだとわかっていたので、 質問をした 、および Nick Craver を教えてくれました。ありがとう、ニック!

解決した質問

これは元々、「これを共有して、誰かが改善を提案できるかどうかを確認させてください」という種類の質問でした。私はまだフィードバックを歓迎しますが、この時点ではかなり完成していると思います。 (短くすることもできますが、読みやすく、簡潔にする必要はありません。)楽しんでください!

更新-jQuery検証の一部になりました

これは、2012年4月3日に 公式にjQuery Validationに追加 でした。

98
Nathan Long

これは、ネイサンの優れたソリューションです。どうもありがとう。

私がやったように誰かがそれを統合するのに苦労する場合に備えて、上記のコードを機能させる方法を以下に示します。

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ファイルを含めることを忘れないでください!

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つのフィールドに入力してください。」というメッセージがすべてのフィールドではなく個別のdivに表示されるようにしました。

フォーム検証スクリプトに入れる

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

patch を提出しましたが、これは現在のバージョンの問題の影響を受けません(これにより、「必須」オプションが他のフィールドで適切に機能しなくなり、現在のバージョンの問題に関する議論が行われます) 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では変数名を$で始める必要がありますが、Javascriptではかなり奇妙(IMHO)です。また、「$ module」を2回、「module」を1回参照していると思いますか?このコードは機能しないはずです。

また、通常のjQueryプラグイン構文であるかどうかはわかりませんが、addMethod呼び出しの上にコメントを追加して、達成することを説明します。上記のテキストの説明を使用しても、fieldset、:filled、value、element、またはselectorが参照するものがよくわからないため、コードを追うのは困難です。おそらくこれのほとんどは、Validateプラグインに精通している人には明らかなので、適切な量の説明について判断を下してください。

おそらく、コードを自己文書化するためにいくつかの変数を分割できます。好む、

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

(あなたのコードのこれらのチャンクの意味を理解したと仮定して!:))

「モジュール」が何を意味するのか正確にはわかりませんが、実際にはこの変数にもっと具体的な名前を付けることができますか?

全体的に素晴らしいコードです!

3

私が取り組んでいるフォームには、これらのようなグループ化された入力を持ついくつかのクローン領域があるため、addMethod関数の1行だけを変更して、require_from_groupコンストラクターに追加の引数を渡しました。

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

このようにして、セレクタ、ID、または要素名を1回渡すことができます。

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

バリデーターは、フォーム内のすべての.reqgrpクラス要素をカウントするのではなく、各フィールドセット内にのみそのクラスを持つ要素に検証を制限します。

2
Andrew Roazen

Rocket Hazmatの答えは、検証が必要な他の定義済みフィールドの問題を解決しようとしていますが、すべてのフィールドに1つの入力が成功すると有効としてマークされます。

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

これで残っている唯一の問題は、フィールドが空で、その後塗りつぶされ、再び空になるEdgeケースです。この場合、エラーはグループではなく単一のフィールドに適用されます。しかし、それはどの周波数でも起こりそうにないようで、その場合でも技術的に機能します。

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()に対応していないことに注意する必要があります。したがって、すべての$をjQueryに置き換えて、たとえばvar $j = jQuery.noConflict()で作業する必要があります

質問があります:組み込みルールのように動作させるにはどうすればよいですか?たとえば、電子メールを入力すると、「有効な電子メールを入力してください」というメッセージは自動的に消えますが、グループフィールドの1つに入力するとエラーメッセージが残ります。

0
Rinat