【问题标题】:jQuery Validate - require at least one field in a group to be filledjQuery Validate - 要求至少填写一个组中的一个字段
【发布时间】:2010-11-21 00:31:17
【问题描述】:

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

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

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

  1. 让您为字段组传入一个选择器
  2. 让您指定必须填充该组的多少才能通过验证
  3. 只要其中一个通过验证,就将组中的所有输入显示为通过验证 验证。 (见最后对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 类添加到错误消息中。您可以按如下方式执行此操作,as demonstrated here

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

万岁!

大喊

现在要大声疾呼 - 最初,我的代码只是盲目地将错误消息隐藏在其他匹配字段上,而不是重新验证它们,这意味着如果还有其他问题(比如“只允许数字并且您输入字母'),在用户尝试提交之前它一直被隐藏起来。这是因为我不知道如何避免上面 cmets 中提到的反馈循环。我知道一定有办法,所以I asked a questionNick Craver 启发了我。谢谢,尼克!

问题已解决

这最初是一个“让我分享一下,看看是否有人可以提出改进建议”之类的问题。虽然我仍然欢迎反馈,但我认为它在这一点上已经很完整了。 (它可以更短,但我希望它易于阅读,不一定要简洁。)所以尽情享受吧!

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

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

【问题讨论】:

  • 另外,请参阅密切相关的规则 - “要么跳过这些字段,要么填写至少 X 个字段” - stackoverflow.com/questions/1888976/…
  • 为什么一个任意输入要负责检查其他输入是否被填充?这没有意义。也许您可以在所涉及的元素中加入一些标记?
  • @dalbaeb - 我稍微澄清了这个例子。不是一个任意输入负责检查其他输入;这是一个组中的每个输入负责检查所有其他输入。
  • 我就是这么想的,非常感谢!
  • 谢谢,这对我有用,但是表单中的其他必填字段现在不再响应,除非它们在检查后获得和失去焦点。 (有人将此添加为您其他问题的答案,但必须标记它,因为它不是答案)。

标签: jquery validation jquery-plugins jquery-validate


【解决方案1】:

这是一个很好的解决方案内森。非常感谢。

这是使上述代码工作的一种方法,以防有人在集成它时遇到麻烦,就像我一样:

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" />

不要忘记包含附加方法.js 文件!

【讨论】:

  • 很高兴它对您有所帮助,并感谢您提供信息。但是,我更喜欢在每个单独的表单上使用一组规则,而不是使用 addClassRules 方法。如果您转到此页面 (jquery.bassistance.de/validate/demo/milk) 并单击“显示此页面上使用的脚本”,您将看到一个示例。我更进一步:我声明了一个名为“rules”的数组,然后分别将它们与 var validator = $('#formtovalidate').validate(rules); 一起使用
  • 另一个想法:您在此处显示的“fillone”类可能有问题。如果在同一张表格上,您需要至少一个部件号和至少一个联系人姓名,该怎么办?只要至少有一个部件号,您的规则将允许 0 个联系人姓名。我认为最好设置像 require_from_group: [1,".partnumber"]...[1,".contactname"] 这样的规则,以确保您验证正确的事情。
【解决方案2】:

很好的解决方案。但是,我遇到了其他必需规则不起作用的问题。对表单执行 .valid() 为我解决了这个问题。

if(!$(element).data('being_validated')) {
  var fields = $(selector, element.form);
  fields.data('being_validated', true); 
  $(element.form).valid();
  fields.data('being_validated', false);
}

【讨论】:

  • 谢谢肖恩,我也遇到了这个问题。但是,此解决方案存在一个问题,当用户第一次访问表单时 - 一旦他填写了第一个 require-from-group 字段,所有其他表单字段都将被验证并因此标记为错误。我解决这个问题的方法是在创建验证器实例之前添加一个 form.submit() 处理程序,我在其中设置了一个标志validator.formSubmit = true。然后在 require-from-group 方法中检查该标志;如果它在那里我做$(element.form).valid();,否则我做fields.valid();
  • 谁能解释这里到底发生了什么?我有一个非常相似的规则,它有效,但我们没有解决重新验证问题(组中的其他字段仍然标记为无效)。但是现在我遇到了即使无效的表单也会提交。如果分组字段有效,则它不会进入 submithandler,如果无效,则进入 invalidHandler 但仍然提交!我会说这是验证插件中的一个相当严重的错误?规则返回有效仅适用于该规则(甚至不是整个字段),那么为什么提交的表单无效?
  • 我进行了进一步调查,并且该组之前的字段未正确验证。我已经将此作为一个单独的问题提出(我发现了部分解决方法):stackoverflow.com/questions/12690898/…
【解决方案3】:

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

我还进行了一些更改,以便“请填写至少 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】:

    我已经提交了一个patch,它没有受到当前版本所存在的问题的影响(因此“必需”选项在其他字段上停止正常工作,有关当前版本问题的讨论在 @987654322 @。

    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】:

      在 PHP 中需要以 $ 开头的变量名,但在 Javascript 中很奇怪(恕我直言)。另外,我相信您将其称为“$module”两次,一次称为“module”,对吗?这段代码好像不行。

      另外,我不确定这是否是正常的 jQuery 插件语法,但我可能会在您的 addMethod 调用上方添加 cmets,以解释您完成了什么。即使有上面​​的文本描述,也很难遵循代码,因为我不熟悉字段集、:填充、值、元素或选择器所指的内容。对于熟悉 Validate 插件的人来说,其中大部分内容可能是显而易见的,因此请判断什么是正确的解释。

      也许您可以分解出一些变量来对代码进行自我记录;喜欢,

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

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

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

      总体来说不错的代码!

      【讨论】:

      • 感谢您的建议 - 我已经澄清了变量名称并将代码分解为更具可读性。
      【解决方案6】:

      这是我对 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 年 4 月就被集成到插件中。
      • 我遇到了 Rocket Hazmat 与验证器一起提供的方法相同的问题。它验证一组字段,但没有验证使用其他方法的其他字段。这个答案是试图解决这个问题。如果您有更好的解决方案,请告诉我。
      • 在开发人员永久解决问题之前,我只是推荐这里认可的任何临时解决方案:github.com/jzaefferer/jquery-validation/issues/412
      【解决方案7】:

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

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

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

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

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

      【讨论】:

        【解决方案8】:

        我遇到了其他规则未与此一起检查的问题,因此我进行了更改:

        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 - 查看我的新答案,它建立在这个答案之上并解决了这个问题。
        【解决方案9】:

        谢谢,内森。你为我节省了大量时间。

        但是,我必须注意到这条规则还没有为 jQuery.noConflict() 做好准备。因此,必须将所有 $ 替换为 jQuery 才能使用,例如 var $j = jQuery.noConflict()

        我有一个问题:我如何让它表现得像内置规则?例如,如果我输入电子邮件,“请输入有效的电子邮件”消息会自动消失,但如果我填写组字段之一,错误消息会保留。

        【讨论】:

        • 你说得对——我没有考虑到无冲突的情​​况。我将来可能会更新它,但如果你愿意,你可以轻松地进行查找和替换。对于你的第二个问题,我没有看到同样的问题。通过快速测试,如果需要某个组中的一个字段,只要我输入任何内容,整个组就会通过该规则。如果需要多于一个,一旦最后一个所需的被填满并失去焦点,整个小组就通过了。这就是你看到的吗?
        • 嗯由于某种原因标记被搞砸了,我没有成功修复它
        • Rinat - 你能简化和缩小问题吗?尝试在不需要 noconflict 更改的更简单的表单上使用我的代码。做你可以测试它的最简单的形式,然后首先让它工作。
        猜你喜欢
        • 1970-01-01
        • 2017-03-12
        • 2010-12-25
        • 2013-01-05
        • 2020-03-08
        • 1970-01-01
        • 2021-01-26
        • 1970-01-01
        相关资源
        最近更新 更多