views:

617

answers:

1

I'm using jQuery Validate and would like to re-validate a group of fields whenever one of them is changed (or possibly, whenever one of them validates successfully). My attempts so far just create infinite loops.

Is this possible, or does the plugin design preclude it?

(Specifically, I've got a method that requires at least X of group Y to be filled out, and as soon as that's true, I'd like all those fields to re-validate. Currently I'm clearing out their error messages with my own code, but that's a hack - it also hides unrelated validation problems until the form is re-submitted.)

+2  A: 

The validate method has a few options to support re-validating on change, namely these:

$(".selector").validate({
  onfocusout: true,
  onkeyup: true,
  onclick: true,
  //rest of your options
});

These all default to false, but should offer the functionality you mention in the question.

Update based on comments: Given a simple test form like this:

<form action="get">
    <div><input type="text" name="part1" class="part"></div>
    <div><input type="text" name="part2" class="part"></div>
    <div><input type="text" name="part3" class="part"></div>
    <div><input type="text" name="part4" class="part"></div>
    <input type="submit" value="Submit" />
</form>

The jQuery would be the following:

jQuery.validator.addMethod("require_from_group", function(value, element, options) {
    var valid = $(options[1], element.form).filter(function() {
        return $(this).val(); asd
    }).length >= options[0];

    if(!$(element).data('reval')) {
        var fields = $(options[1], element.form);
        fields.data('reval', true).valid();
        fields.data('reval', false);
    }
    return valid;
}, jQuery.format("Please fill out at least {0} of these fields."));

$("form").validate({
    rules: {
        part1: { require_from_group: [2,".part"] },
        part2: { require_from_group: [2,".part"] },
        part3: { require_from_group: [2,".part"] },
        part4: { require_from_group: [2,".part"] }
    }
});​

You can play with a demo here, see if this is what you're after: http://jsfiddle.net/mhmBs/

The method here uses .data() to let an element know not to cause an infinite loop. The actual element that is being edited, on blur (normal cause for validation triggers) re-validates only the other elements in the selector you specified in the group, so not the whole form like my comment...it's only triggering it on the minimum number of elements. ​

Nick Craver
Each of these options has to do with re-validating the current field. I'm trying to re-validate fields related to the current one. In other words, to say 'if this field passes validation, validate the two other fields of the same class' or something similar.
Nathan Long
@Nathan - Is triggering the full form to re-validate an option, upon change of these fields?
Nick Craver
@Nick - sure, that would work.
Nathan Long
@Nathan - Updated with what I *think* is what you're after, let me know if not.
Nick Craver
@Nick - this is great! I added to your demo to verify that when this test passes, it doesn't clear any other errors, such as 'this must be a number.'
Nathan Long
@Nathan - Excellent! Glad this solution works for you :)
Nick Craver
@Nick - I wasn't familiar with `.data()` - very interesting. Thanks for the lesson and for the demo of your solution. You have thoroughly earned the rep for this one.
Nathan Long
@Nick - P.S. - why did you select both the selector that was passed in AND element.form?
Nathan Long
@Nathan - It's not selecting both, but rather that selector only inside the current form, it's the `$(selector, context)` style, by default if you don't pass one, it's `document`, so this: `$(selector)` is *really*: `$(selector, document)`, which internally calls: `$(document).find(selector)`. In this case we're telling to to search for the selector, but **only** inside the current form, which with the validation plugin (being per-form) is the only elements the rule applies to. If you look inside the validation plugin, you'll see lots of `selector, element.form)` as well, same reason.
Nick Craver
@Nick - gotcha. I thought you were doing this: http://api.jquery.com/multiple-selector/. I haven't seen that way of giving context and don't see it at api.jquery.com - the only way I knew was like `$('form#foo input')`. Do you have to pass in two jQuery objects for this context limiter? Also, what's up with `element.form`? Is that a generalizable thing? I originally did `$(element).parents('form')` and don't quite get your way yet.
Nathan Long
@Nathan - You can read about the `$()` [overloads here](http://api.jquery.com/jQuery/). It can take a number of things, a DOM element (like this case), a document, or a jquery object. `element.form` [is a DOM element property](http://msdn.microsoft.com/en-us/library/ms533768.aspx), just base javascript. The closest thing in jQuery would be: `$(element).closest('form').get(0)`
Nick Craver
@Nick - so great. Thanks again for all the help; this has been very educational.
Nathan Long
@Nathan - Welcome :) I might end up using this same approach somewhere, thanks for sharing it in the original question...I'd update that with your final result, it is a *very* common situation.
Nick Craver
@Nick - done. I sacrificed the conciseness of your code for a more explanatory approach, once I figured out how that 'reval' part worked. :) FYI - I have a related 'fill X of these or just skip them all' rule that I will also update with your approach. Maybe it will be useful for you, too. It's here: http://stackoverflow.com/questions/1888976/jquery-validate-either-skip-these-fields-or-fill-at-least-x-of-them
Nathan Long
Why the validator overrides the first items to validate, group is into last part.
text