views:

1528

answers:

3

UPDATE:
I've come up with a solution to this which I've written as the accepted answer below.
Another option is to apply validation rules to all elements initially, add an ".invalid" class to elements you don't want to validate. Swap this class in & out of elements you want to validate as needed.


I have a form that toggles what input elements are visible. I want to validate only the visible elements on the form.

I'm having a hard time getting this to function correctly. I want to disable validators for non-visible elements and I can't seem to figure out the best way to do this. Any insight to what may be wrong with my code, or my approach would be appreciated.

When visibility is toggled, I've tried a few things:

  • Calling $("form").rules("remove") to clear all existing validation rules. This throws an "undefined" JavaScript exception.
  • Calling $("form").validation(...options...) for the visible elements, hoping this would overwrite the rules. This only allows the first group that is validated to work. The second panel can not be validated.
  • Unbinding the submit handler before calling the new validation() method. This didn't do what I would have thought. It removes all validation (seemingly) permanently and the form submits without validation.
  • Clearing out the validation object with $.removeData($('form'), 'validator') before trying to call the validator again. This also doesn't work.
  • This is in an ASP.NET site, so using multiple <form /> tags is out of the question since that would break the page.

I'm sort of stumped on how I can make this work. You can see a complete working demo of what I have at http://jsbin.com/ucibe3, or edit it at http://jsbin.com/ucibe3/edit. I've tried to strip it down to just the code that causes the bug.

Here are the key pieces of my code (use above links for complete working page)

HTML:

<td id="leftform">
    Left Form<br />
    Input 1: <input type="text" name="leftform_input1" /><br />
    Input 2: <input type="text" name="leftform_input2" /><br />
    <input type="submit" name="leftform_submit" value="Submit Left" />
</td>
<td id="rightform" style="visibility:hidden">
    Right Form<br />
    Input 1: <input type="text" name="rightform_input1" /><br />
    Input 2: <input type="text" name="rightform_input2" /><br />
    <input type="submit" name="rightform_submit" value="Submit Right" />
</td>

JavaScript:

$('#toggleSwitch').click(function() {
    if ($('#leftform').css("visibility") !== "hidden") {
        $('#leftform').css("visibility", "hidden");
        $('#rightform').css("visibility", "visible");
        $('form').validate({
            rules: {
                rightform_input1: { required: true },
                rightform_input2: { required: true }
            },
            messages: {
                rightform_input1: "Field is required",
                rightform_input2: "Field is required"
            }
        });
    } else {
        $('#rightform').css("visibility", "hidden");
        $('#leftform').css("visibility", "visible");
        $('form').validate({
            rules: {
                leftform_input1: { required: true },
                leftform_input2: { required: true }
            },
            messages: {
                leftform_input1: "Field is required",
                leftform_input2: "Field is required"
            }
        });
    }
});
A: 

While there are other ways to do this ASP.net does allow you to use multiple form tags so long as they are not all rendered - so if you use a multi view to only display the relevant content you can use multiple forms.

It tends to clean up validation and submit handling quite a bit.


Alternatively jquery does allow :visible to be added to your selector, so you could potentially only run the selector on visible elements and sidestep the issue that way depending on how they are hidden.

How :visible is calculated was changed in jQuery 1.3.2. Element assumed as visible if it and its parents consumes space in document. CSS visibility isn't taken into account. The release notes outline the changes in more detail.


Here is a better / simpler solution from another post on stack overflow-

Apparently the validation plug-in allows you to specify :visible in your rules. Here is the post-

http://stackoverflow.com/questions/1316602/advanced-jquery-validation-avoiding-validations-on-certain-conditions/1316818#1316818

apocalypse9
I would consider using multiple form tags as you mentioned, however other than validation, the page is complete and there's a lot going on in the page so it would take a lot of restructuring of the page to make this solution work.
Dan Herbert
A: 

The "required" declarations inside of the validate.rules hash do not have to be statically declared as "true". You can actually supply an anonymous function instead which can check for the visibility of something and return true or false depending on the answer.

$('form').validate({
rules: {
  rightform_input1: { 
    required: function(){
              $('#leftform').css("visibility") !== "hidden"
              } },
  rightform_input2: {
    required: function(){
              $('#leftform').css("visibility") !== "hidden"
              } }
  }
});
emills
+3  A: 

This uses a feature of the validator that is not well documented in the API (and by this I mean it isn't documented at all), however since it is a fundamental part of how the validator works, it is not likely to be removed even if it is undocumented.

Once you've initialized the jQuery validator, you can get access to the validator object again by calling the validate() method on the form object you applied the validator to. This validator object has a settings property, which stores the default settings, combined with the settings you applied to it in initialization.

Assuming I initialize the validator like this:

$('form').validate({
    rules: {
        leftform_input1: { required: true },
        leftform_input2: { required: true }
    },
    messages: {
        leftform_input1: "Field is required",
        leftform_input2: "Field is required"
    }
});

I can then get those exact settings out of the validator by doing the following:

var settings = $('form').validate().settings;

I can then easily work with this settings object to add or remove validators for the form.

This is how you would remove validation rules:

var settings = $('form').validate().settings;
delete settings.rules.rightform_input1;
delete settings.messages.rightform_input1;

And this is how you would add validation rules:

var settings = $('form').validate().settings;
settings.rules.leftform_input1 = {required: true};
settings.messages.leftform_input1 = "Field is required";


Here is a working solution for the scenario in my question. I use jQuery's extend() method to overwrite the rules and messages properties of the validate object, which is how I toggle between the two panels.

$('#toggleSwitch').click(function() {
    var settings = $('form').validate().settings;
    var leftForm = $('#leftform');
    var rightForm = $('#rightform');

    if (leftForm.css("visibility") !== "hidden") {
        leftForm.css("visibility", "hidden");
        rightForm.css("visibility", "visible");
        $.extend(settings, {
            rules: {
                rightform_input1: { required: true },
                rightform_input2: { required: true }
            },
            messages: {
                rightform_input1: "Field is required",
                rightform_input2: "Field is required"
            }
        });
    } else {
        rightForm.css("visibility", "hidden");
        leftForm.css("visibility", "visible");
        $.extend(settings, {
            rules: {
                leftform_input1: { required: true },
                leftform_input2: { required: true }
            },
            messages: {
                leftform_input1: "Field is required",
                leftform_input2: "Field is required"
            }
        });
    }
});
Dan Herbert
I just spent a few hours trying to do something similar, glad you posted your solution. I found the rule() method supports add/remove rules but I could not find any info on adding rules and messages together. Your solution in using the settings object worked great! Thanks!
Zachary