Thursday, August 5, 2010

jQuery Validation Plugin and Uni-Form

I'm using Uni-form on my current project and it is pretty structured in the way elements are nested and classed (and rightly so).  One thing we are taking advantage of is the error notification.  As opposed to giving a small error message, the field div is colored a light shade of red.  Seems like a positive user experience to me as there is no question which fields are in error.
I also usually perform validation via a javascript class I've been using for awhile, but it is clunky and time consuming.  Since I'm embracing jQuery on this project I knew there had to be a good validation plugin.  The one I am using is named simply jQuery plugin: Validation. The docs are decent and the plugin has been around for a few years.  It also validates most of the types a web application would need.  Another cool thing is that it allows field grouping for validation which I could see being handy.

The default behavior is pretty standard in that it will add an error class to the field and append a label with the error message.  Each error message may be overwritten by validation type across the entire validator, or right down to the element level.  9 times out of 10 this would be awesome.  Since I was using Uni-form's error schema, this didn't work for my application.  In Uni-form, a single form element is grouped together with its label and a metadata hint like the following:
<div class="ctrlHolder">
      <label for="">Text input</label>
      <input id="txt" name="txt" value="" size="35" class="textInput" type="text">
      <p class="formHint">This is a form hint.</p>
    </div>
What I needed was to add an error class to the parent div that contains the text input control.  I tried several different methods of achieving this, but the one that worked for me was to use the 'highlight' and 'unhighlight' configuration properties of the validator.  The setup for the validator looks like this:
// load the validator when the DOM is ready
  $(function () {
    $.validator.messages.required = '';
    var validator = $('form').validate({
      // disable checking on every event except submit      
      onkeyup: false,
      onfocusout: false,
      onclick: false,
      debug: true,
      // define the rule for our text field to be required
      rules: {
        txt: 'required'
      },
      // function to call when the form has passed validation and ready to be submitted
      submitHandler: function () {alert('form submit!');},
      errorPlacement: function () {},
      highlight: function(element, errorClass, validclass) {
        $(element).parent().addClass('error');
      },
      unhighlight: function(element, errorClass, validclass) {
        $(element).parent().removeClass('error');
      }
    });
  }) 
Getting the parent div was easy enough using jQuery and the element passed back into the hightlight and unhighlight methods. We just had to add a class when the error is caught, and remove the class when the error has been cleared.

You'll also note that I needed to disable the default behavior which appends a label containing the error message after the form control.  This was achieved by passing an empty function to the errorPlacement configuration property as seen on line 16 above.

One caveat to using the Validate plugin is that you have to have a name attribute on all form fields (at least those you want to validate, all if you don't want to see any errors in debug mode). I generally only include an 'id' attribute, so this hung me up for a bit (thanks @boyzoid)

The complete example can be downloaded here.

5 comments:

  1. I'd recommend always having a name attribute anyway. I always prefix the id (for example, id="fEmail") so they're less likely to collide with ids elsewhere on the page...

    ReplyDelete
  2. Great post! It worked like a charm for me. The only problem is when I called the parent() function on a checkbox list. This script applied an error class to the ul element above the input field instead of the paragraph element.

    I fixed it by using replacing .parent() with .closest('.ctrlHolder').

    ReplyDelete
  3. I'm trying to use this script with another jquery slider script.
    Being somewhat new to javascript I'm wondering which script needs the (document).ready and which order, if it even matters, they need to be in.

    My testing site is here: www.thegallerymusic.com/testing

    I appreciate the help!

    ReplyDelete
  4. @Dave Not quite sure where your actual form is on the test site, but my gut says it probably doesn't matter in which order they are run.

    ReplyDelete
  5. Thanks... the errorPlacement: function () {} was exactly what I was looking for.

    ReplyDelete