jQuery Validation Groups for ASP.Net Webforms

I recently published a plug-in via Nuget that makes using jQuery Validation with ASP.Net WebForms a little more elegant. There is a limitation when using jQuery Validation with WebForms where if you have two logical 'form' groups in an ASP.Net WebForm when you click 'submit' in one section the entire page will be validated.

Consider the below example. There are two sections, one to login and the other to sign up. I want the validation to only fire for the "First Name" field when the "Sign Up" button is clicked.

If I click on the "sign up" button, this happens:

Screen Shot 2012 08 25 at 16 25 38 Clicking on the "sign up" button also triggers the validation for the login section.

Why not use the standard ASP.Net Validation Controls?

The standard ASP.Net validation controls e.g. required field validator get around this problem with the concept of validation groups. The downside to using the standard ASP.Net controls is that you have to add the obtrusive markup into your page e.g:

<p>
 <asp:Label ID="uiFirstName" runat="server" AssociatedControlID="uxFirstName" Text="First name:"></asp:Label>
 <asp:TextBox ID="uxFirstName" runat="server" CssClass="required"></asp:TextBox>
 <asp:RequiredFieldValidator runat="server" ID="valFirstName" ControlToValidate="uxFirstName"></asp:RequiredFieldValidator>
</p>

Using jQuery Validation allows you to attach validation bindings either via JavaScript or using CSS class names, leaving a cleaner separation of concerns in the HTML markup. This is one of the main reasons as to why this plugin has become so popular.

Dave Ward wrote a great blog post addressing this problem where he offers a solution that will give us "validation groups" using jQuery Validation. This solution is great, but it leaves a side effect of having to write more JavaScript where there are multiple form sections on a page. I wanted the best of both worlds and something that could be used right out of the box for WebForm developers - which is what this plug-in aims to achieve.

How to use

If you are unfamiliar with jQuery Validation, please take a look at the documentation. This plug-in sits on top of jQuery Validation so all the features work as standard.

To set up jQuery Validation you have to attach the validate event to the form, as below:

        $(function() {
            $("#aspForm").validate();
        });

To make use of the jQuery Validation with ASP.Net WebForms plugin simply download the package via Nuget or get the JavaScript source file from Github (links are below) and change the event binding to the below:

        $(function() {
            $("#aspForm").validateWebForm();
        });

If we have multiple validation groups on a form, we can set this up using CSS names. Simply wrap an element around your validation section and give it a class name of "form". Next, on the submit action that should trigger the validation event just add the CSS class name "submit". That's it! Here is a basic example:

<fieldset class="form">

....

     <asp:Button ID="uxRegister" runat="server" Text="Sign Up" CssClass="submit" />

</fieldset>

Code Example

Here is a working example from the screen shot that uses the plug-in.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
 <title>Multiple Form Validation</title>
 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
 <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.9/jquery.validate.min.js"></script>
 <script type="text/javascript" src="jquery.validation.net.webforms.min.js"></script>
 <script type="text/javascript">
 $(function() {
 $("#aspForm").validateWebForm();
 });
 </script>
 <style type="text/css">
 .error {
 color: red;
 }
 </style>
</head>
<body>
 <form id="aspForm" runat="server">
 <fieldset class="form" id="signup">
 <div class="something">
 <ul></ul>
 </div>
 <legend>Sign Up</legend>
 <p>
 <asp:Label ID="uiFirstName" runat="server" AssociatedControlID="uxFirstName" Text="First name:"></asp:Label>
 <asp:TextBox ID="uxFirstName" runat="server" CssClass="required"></asp:TextBox>
 </p>
 <p>
 <asp:Button ID="uxRegister" runat="server" Text="Sign Up" CssClass="submit signup" />
 <asp:Button ID="uxCancelRegister" runat="server" Text="Cancel" />
 </p>
 </fieldset>
 <fieldset class="form" id="login">
 <legend>Login</legend>
 <p>
 <asp:Label ID="uiUserName" runat="server" AssociatedControlID="uxUserName" Text="User name:"></asp:Label>
 <asp:TextBox ID="uxUserName" runat="server" CssClass="required email"></asp:TextBox>
 </p>
 <p>
 <asp:Button ID="uxLogin" runat="server" Text="Login" CssClass="submit login" />
 <asp:Button ID="uxCancelSignUp" runat="server" Text="Cancel" />
 </p>
 </fieldset>
 </form>
</body>
</html>

Download from Nuget

PM> Install-Package jQuery.Validation.WebForms

See the Nuget project page.

Source code on Github

You can download the full source here.

Future Changes

I have another enhancement in the pipeline, which will be to have different display settings for each validation section. Currently there is still the restriction to a single form element i.e. one set of display options per form.

If you have any feature requests or run into any issues with this plug-in please leave a comment.

20 comments:

  1. Great plugin. One issue I'm encountering. Let's say you have 3 forms on the page. You fill out the 2nd one (according to the order in the html markup), after you fill out all (or some) the fields, instead of clicking the actual submit button you press enter on the keyboard while still in focus in one of your form's input fields. What happens it the form above the second one gets validated too and all the error messages for that first form show up. Any forms below the ones input field focus don't trigger. Any way to get around this problem?

    ReplyDelete
    Replies
    1. Are the containing divs that represent the 'form' nested!

      Delete
    2. No, one form I have is a global site search and it's located at the top of the page in the header section. The other form is a registration form located down in the main content area of the page. Basically once I'm going through the registration form and click the return key on the keyboard while focused on one of the input fields within the second form, the site search form error triggers an error that that field is empty.

      Same issue if I have 2 sibling forms and I try to submit the 2nd form via a return key press.

      Delete
    3. Understood. I will try and update the source in the next 24 hours.

      Delete
    4. Hi, this should be corrected now. Either fetch a new version from the Github repository or try the update Nuget package. Thanks

      Delete
    5. Thanks Brad, this works great!

      Delete
  2. Hello Brad, I think I found another issue. Same deal as before with multiple forms on the page but this time it only happens when you're focused on an input with a type="password". If you try to submit the form via keydown enter from that input, any form above will get triggered for validation. Would you mind checking this out? The password field is usually the last item on a form as well so this might be more of an issue than what it sounds like.

    ReplyDelete
    Replies
    1. Good catch, that was a silly bug! It would have only worked with text fields. I've updated Github and Nuget. Thanks again for taking the time to let me know there was an issue!

      Delete
  3. Loving this cheers Brad, I've been wanting something to do this for a while now!

    ReplyDelete
  4. Hi Brad, this is just the thing I needed for my old asp.net webforms. I am having trouble using the submitHandler option. it seems to ignore it. When change back to validate() it works. Any ideas? Cheers.

    ReplyDelete
  5. This is great cheers Brad. Just one thing I am wondering. Why have you forced the onsubmit to to be false? Doing this removes the submitHandler function. which I need for my ajax submission. If I take this out, I can always set it in the options anyway.

    ReplyDelete
    Replies
    1. That's there to stop the validate event treating all the controls on the page as an entire form (which technically it is) which is not what we want otherwise we are back to the problem described in the first screen shot of this post! What are you using for AJAX i.e. standard .Net AJAX controls?

      Delete
    2. Example: I have an email signup form. The client side checks if I have filled out the relevant fields and checked if it is a valid email. Once that is valid, I then fire off my ajax request to check if the user has already signed up. this then returns the results, if success I can then post the form, else show my error message.

      Delete
    3. Understood, I'll take a look but it might take me a few days to get to this. In the meantime, you could experiment with something along the lines of:

      $("form").validate().submitHandler(); <-- maybe this will invoke the function you setup on the document.ready??

      In the source, you could add something here:

      isValid == // result from submitHandler response

      /* then the existing code should jump in */

      if (!isValid) {
      event.preventDefault();
      }

      Let me know how it goes, thanks for taking the timeout to mention this.

      Delete
    4. Thanks for this plugin Brad! I have the same issue. I want to use the submitHandler to submit my form via Ajax. I don't understand your solution above. Would you have an exemple?

      I was also trying to do something else. When I click on my submit button I want to check if the related form is valid. How would I do that?
      When I do it the usual way I get the same result as if I was not using your plugin.

      Thanks for you help!

      Delete
    5. Hi Anon,

      I'm taking a look at the submit handler problem now.

      As for your second question, if you click on a button and you want to know if that "validation group" is valid, simply add something like this:

      val groupIsValid = validateAndSubmit("#my-button-id");

      That method will find the parent div (with "form" class attribute) to which the submit button belongs and validate all the controls inside.

      Keep in mind that the controls you want to validate as a logical need to be in their own container with the class name "form" as an attribute e.g.

      <div class="form">


      &;tl/div>

      Hope this helps!

      Delete
    6. The HTML parser messed up my example - it was supposed to be a DIV container with the class FORM with the submit button inside.

      Delete
  6. OK thanks, that response got me thinking & I have reached a solution. A bit hacky and not great but it works. It just means I will have to add a lot of event listeners.

    I have added an else condition in your !isValid statement.

    if (!isValid) {
    event.preventDefault();
    }else{
    group.trigger('group-valid');
    }

    then in my code before I initiate the validate on the form if add a listener

    //custom event listener when group is valid
    $j('.newsletter-signup .form').bind('group-valid', $j.proxy(this, 'postData'));

    // validate webform
    this.$globalForm.validateWebForm();

    postData: function(){
    //ajax form post
    }

    If you do manage to get it worked into your plugin then It would be great to see your implementation. meanwhile I will use this method.

    Cheers
    Andrew

    ReplyDelete
    Replies
    1. Hi Andrew, a little later than I said but I finally got around to taking a look at this. Thanks for your code example.

      I've made a start on what I think will work, does this solve your problem?https://github.com/bbraithwaite/JQueryValidationForWebForms/blob/master/SubmitHandlerExample.aspx

      This just ensures that the submitHandler function is invoked (if it has been setup).

      NB I've yet to publish an update Nuget package for this, I will soon, but for now you can grab the latest code from the Github Repo.

      Delete
    2. Yes thanks, works a treat. Funny, I just left my desk to think about it a bit more and realised I could call the original function there. Turns out that's what you had done. Must have been staring at it too long. Cheers for the fast reply.

      Delete

Got a Question? What do you think? Your comments are appreciated: