Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
422 views
in Technique[技术] by (71.8m points)

validation - jQuery Validate - "Either skip these fields, or fill at least X of them"

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...