代码之家  ›  专栏  ›  技术社区  ›  Patrick McElhaney

jquery validate-要求在一个组中至少填写一个字段

  •  98
  • Patrick McElhaney  · 技术社区  · 6 年前

    我在使用优秀的jquery Validate Plugin 以验证某些表单。在一个表单上,我需要确保用户至少填写一组字段中的一个。我想我有一个很好的解决方案,想和大家分享。 请提出你能想到的改进建议。

    我找不到做这件事的内置方法 Rebecca Murphey's custom validation method ,这很有帮助。

    我从三个方面改进了这个:

    1. 让您传入字段组的选择器
    2. 指定要通过验证必须填充的组数
    3. 在组中的所有输入之一通过验证后立即将其显示为通过验证 验证。(见呼喊 Nick Craver 最后)

    所以你可以说 “必须至少填充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 成功验证后返回错误消息。你可以这样做, as demonstrated here .

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

    在上面链接的演示中,我使用css为每个 span.error 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."));
    

    万岁!

    大声叫喊

    现在,我的代码只是盲目地将错误消息隐藏在其他匹配字段上,而不是重新验证它们,这意味着如果出现了另一个问题(例如“只允许数字,而您输入了字母”),它将被隐藏,直到用户尝试提交为止。这是因为我不知道如何避免上述评论中提到的反馈循环。我知道一定有办法,所以 I asked a question 尼克克拉弗 启发了我。谢谢,尼克!

    问题已解决

    这本来是一个“让我分享这个,看看是否有人可以建议改进”的问题。虽然我仍然欢迎反馈,但我认为在这一点上已经很完整了。(它可以短一些,但我希望它读起来容易,不一定简洁。)所以好好享受吧!

    update-now是jquery验证的一部分

    这是 officially added to jQuery Validation 4/3/2012。

    9 回复  |  直到 11 年前
        1
  •  21
  •   Gerasimos    15 年前

    那是个很好的解决办法内森。谢谢。

    下面是一种使上述代码工作的方法,以防有人像我一样在集成时遇到问题:

    内部代码 附加方法.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" />
    

    别忘了包含额外的-methods.js文件!

        2
  •  6
  •   Sean    14 年前

    很好的解决方案。但是,我有一个问题,就是其他规定的规则不起作用。对表单执行.valid()为我修复了此问题。

    if(!$(element).data('being_validated')) {
      var fields = $(selector, element.form);
      fields.data('being_validated', true); 
      $(element.form).valid();
      fields.data('being_validated', false);
    }
    
        3
  •  4
  •   Walter Kelly    13 年前

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

    我还做了一些更改,以便“请至少填写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 method addmethod函数

     if(validOrNot){
        $("#form_error").hide();
    }else{
        $("#form_error").show();
    }
    ......
    }, jQuery.format(" &nbsp;(!)"));
    
        4
  •  4
  •   Aleksi Yrttiaho    11 年前

    我已经提交了 patch 这不会受到当前版本所带来的问题的影响(即“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."));
    
        5
  •  3
  •   Michael Gundlach    15 年前

    在php中,以$开头的变量名是必需的,但在javascript中却是非常奇怪的(imho)。另外,我相信你会把它称为“$module”两次和“module”一次,对吧?看来这段代码不应该起作用。

    另外,我不确定这是否是正常的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(...)
    

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

    我不太清楚“module”到底是什么意思——你能给这个变量起个更具体的名字吗?

    总的来说,代码不错!

        6
  •  2
  •   Andrew Roazen    15 年前

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

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

    这样,选择器、id或元素名就可以传递一次:

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

    验证器会将验证限制为每个字段集中只有该类的元素,而不是尝试计算表单中所有.reqgrp类元素。

        7
  •  2
  •   squarecandy    12 年前

    这是我对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."));
    

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

        8
  •  1
  •   Rocket Hazmat    13 年前

    我对其他没有与此相关联的规则有问题,因此我更改了:

    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."));
    
        9
  •  0
  •   Rinat    15 年前

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

    但是,我必须注意到这个规则还没有jquery.noconflict()就绪。因此,必须用jquery替换所有的$ var $j = jQuery.noConflict()

    我有一个问题:我如何让它表现得像内置规则?例如,如果我输入email,则消息“please enter valid email”将自动消失,但如果我填写组字段之一,则错误消息将保留。