jQuery Validate - “Either skip these fields, or fill at least X of them”

匿名 (未验证) 提交于 2019-12-03 02:14:01

问题:

I'm using the jQuery Validation plugin on my forms. I have some groups of fields that are optional, but need to be either "all or nothing" - if you fill one input in a group, you must fill all of them.

For example, imagine the user can input one location - street, city, state, and zip - or multiple locations. You don't have to enter a second location, but if you do, it's not OK to just give the city; I need the state and zip for that one too.

To solve this problem, I wrote a custom rule - really just a tiny tweak of my previous rule, require_from_group. This one is called skip_or_fill_minimum. Here's how you'd use it:

var validationrules = {   rules: {     location2address: {     skip_or_fill_minimum: [4,'.location2']   }   //This input will validate if all 4 `.location2` inputs are filled,   //or if all of them are left blank }  var validator = $("#formtovalidate").validate(validationrules);   

Here's the code for the custom rule:

jQuery.validator.addMethod("skip_or_fill_minimum", function(value, element, options) {     var numberRequired = options[0];     var selector = options[1];     //Look for our selector within the parent form     var numberFilled = $(selector, element.form).filter(function() {          // Each field is kept if it has a value          return $(this).val();       }).length;       var valid = numberFilled >= numberRequired || numberFilled === 0;      //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 all the     //elements check all the others which check all the others which     //check all the others...     //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);       //.valid() means "validate using all applicable rules" (which includes this one)       fields.data('being_validated', true);       fields.validate(); //Changed from fields.valid();       fields.data('being_validated', false);     }     return valid;     // {0} below is the 0th item in the options field     }, jQuery.format("Please either skip these fields or fill at least {0} of them.")); 

I'm primarily posting this so that others who search the web for a solution will find it. Still - does anyone see a way to improve this?

Update - now part of jQuery Validation

This was officially added to jQuery Validation on 4/3/2012.

回答1:

My shortened and "optimized" version (more chaining + use of validator custom provided selector). And hopefully makes less selection operations.

Still suffers from the same bugs your rule does -- what I mean is that the removing of the error class and the error labels on all elems which match our selector is not really correct. Just imagine you require 4 fields to be set but there are 5. User fills 4 fields. The fifth he leaves unfilled but on that field you additionally set required and email. This way the error-message for the email too gets removed although he didn't resolve it yet.

jQuery.validator.addMethod("skip_or_fill_minimum", function(value, element, options) {     var elems = $(element).parents('form').find(options[1]);     var numberFilled = elems.filter(':filled').size();     if (numberFilled >= options[0] || numberFilled == 0) {         elems.removeClass('error');         elems.next('label.error').not('.checked').text('').addClass('checked');         return true;     } else {         elems.addClass('error');     } }, jQuery.format("Please either skip these fields or fill at least {0} of them.")); 


回答2:

It took me quite a while to find the fix mentioned above for the problem where fields above the group would not get validated.

It would be good if the change of .valid() to .validate() was added to the closely related questions - here and here as many people are asking about it.

This fix though stops all the groups labels from being shown or removed on key or blur events. I found jitter's if else statement above to be very helpful in solving that.

note: stackoverflow has help me find many answers in the past, but this is my first actual contribution. Thanks all for bring together this solution.



回答3:

This is my first post to SO, so I hope I'm doing this correctly, but there appears to be an error in the accepted solution. I have added the closing bracket for the location2address block (or the rules block, depending on how you look at it).

var validationrules = {   rules: {     location2address: {       skip_or_fill_minimum: [4,'.location2']     }   }   //This input will validate if all 4 `.location2` inputs are filled,   //or if all of them are left blank } var validator = $("#groovetime_entry").validate(validationrules);  


回答4:

I needed something slightly different. It is a search form with Either or Both first or last name AND/OR 1 of 4 other search criteria.

I wanted them to either skip or ONLY enter one (or x). I modified the code above:

    jQuery.validator.addMethod("skip_or_fill_only_x", function (value, element, options) {     var numberRequired = options[0];     var selector = options[1];     var numberFilled = $(selector, element.form).filter(function () {         return $(this).val();     }).length;     //Changed this line from "numberFilled >=" to "numberFilled ==="     var valid = numberFilled === numberRequired || numberFilled === 0;     if (!$(element).data('being_validated')) {         var fields = $(selector, element.form);         fields.data('being_validated', true);         fields.validate();         fields.data('being_validated', false);     }     return valid; //Adjusted message }, jQuery.format("Please either skip these fields or only fill {0} of them.")); 

the full implementation looks like this:

    $('#advancedSearchForm').validate({     errorLabelContainer: '#advancedErrors',     rules: {         FirstName: {             require_from_group: [1, '.mygroup']         },         LastName: {             require_from_group: [1, '.mygroup']         },         countryCodeAdvanced: {             skip_or_fill_only_x: [1, '.location2']         },         location: {             skip_or_fill_only_x: [1, '.location2']         },         PositionTitle: {             skip_or_fill_only_x: [1, '.location2']         },         FunctionalArea: {             skip_or_fill_only_x: [1, '.location2']         }     },     groups: {         mygroup: "FirstName LastName",         location2: "countryCodeAdvanced location PositionTitle FunctionalArea"     } });   jQuery.validator.addMethod("skip_or_fill_only_x", function (value, element, options) {     var numberRequired = options[0];     var selector = options[1];     var numberFilled = $(selector, element.form).filter(function () {         return $(this).val();     }).length;     var valid = numberFilled === numberRequired || numberFilled === 0;     if (!$(element).data('being_validated')) {         var fields = $(selector, element.form);         fields.data('being_validated', true);         fields.validate();         fields.data('being_validated', false);     }     return valid; }, jQuery.format("Please either skip these fields or only fill {0} of them.")); 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!