jQuery Validate - 要求填充组中的至少一个字段

时间:2009-08-19 16:08:12

标签: jquery validation jquery-plugins jquery-validate

我正在使用优秀的jQuery Validate Plugin来验证某些表单。在一种形式上,我需要确保用户填写一组字段中的至少一个。我想我有一个很好的解决方案,想分享它。 请提出您可以考虑的任何改进建议。

找不到内置的方法,我搜索并找到了Rebecca Murphey's custom validation method,这非常有帮助。

我通过三种方式对此进行了改进:

  1. 让您传入字段组的选择器
  2. 允许您指定必须填充该组的数量才能通过验证
  3. 一旦其中一个输入通过,就将组中的所有输入显示为通过验证 验证。 (最后请注意Nick Craver。)
  4. 所以你可以说“至少必须填写与选择器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类。您可以按照以下方式执行此操作,as demonstrated here

    success: function(label) {  
            label.html(" ").addClass("checked"); 
    }
    

    与上面链接的演示一样,我使用CSS为每个span.error提供一个X图像作为其背景,除非它有类.checked,在这种情况下它会得到一个复选标记图像。< / p>

    到目前为止,这是我的代码:

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

    万岁!

    喊出来

    现在为了那个喊叫 - 最初,我的代码只是盲目地隐藏其他匹配字段上的错误消息而不是重新验证它们,这意味着如果还有另一个问题(比如'只允许数字而你输入了字母'),它被隐藏,直到用户试图提交。这是因为我不知道如何避免上述评论中提到的反馈循环。我知道必须有办法,所以I asked a questionNick Craver启发了我。谢谢,尼克!

    解决问题

    这最初是“让我分享一下,看看是否有人可以提出改进”的问题。虽然我仍然欢迎反馈,但我认为这一点非常完整。 (它可能更短,但我希望它易于阅读,而不一定简洁。)所以只是享受!

    更新 - 现在是jQuery验证的一部分

    2012年4月3日这是officially added to jQuery Validation

9 个答案:

答案 0 :(得分: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文件!

答案 1 :(得分: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);
}

答案 2 :(得分:4)

谢谢肖恩。这解决了我对代码忽略其他规则的问题。

我还进行了一些更改,以便“请填写至少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;(!)"));

答案 3 :(得分:4)

我提交的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 :(得分:3)

在PHP中需要使用$启动变量名称,但在Javascript中非常奇怪(恕我直言)。另外,我相信你把它称为“$ module”两次,“模块”一次,对吧?似乎这段代码不起作用。

另外,我不确定这是否是正常的jQuery插件语法,但我可能会在你的addMethod调用上面添加注释,解释你完成了什么。即使你上面的文字描述,也很难遵循代码,因为我不熟悉哪个字段集:fill,value,element或selector引用。对于熟悉Validate插件的人来说,大多数情况可能是显而易见的,因此请使用判断正确解释的内容。

也许你可以打破几个变量来自我记录代码;等,

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

(假设我确实理解了这些代码块的含义!:))

我不确定“模块”是什么意思,实际上 - 你可以给这个变量一个更具体的名字吗?

好的代码,整体来说!

答案 5 :(得分:2)

因为我正在处理的表单有几个具有分组输入的克隆区域,所以我向require_from_group构造函数传递了一个额外的参数,只改变了addMethod函数的一行:

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

这样一个选择器,ID或元素名称可以传递一次:

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

并且验证器将验证仅限于每个字段集中具有该类的元素,而不是尝试计算表单中的所有.reqgrp分类元素。

答案 6 :(得分: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."));

现在唯一剩下的问题是边缘情况,其中字段为空,然后填充,然后再次为空......在这种情况下,错误将应用于单个字段而不是组。但这似乎不太可能以任何频率发生,并且在这种情况下它仍然在技术上有效。

答案 7 :(得分: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."));

答案 8 :(得分:0)

谢谢,内森。你救了我很多时间。

但是,我必须注意到这个规则不是jQuery.noConflict()准备好的。因此,必须使用jQuery替换所有$,例如var $j = jQuery.noConflict()

我有疑问:我如何使其表现得像内置规则?例如,如果我输入电子邮件,则“请输入有效电子邮件”消息会自动消失,但如果我填写其中一个组字段,则会显示错误消息。