Skip to content

Instantly share code, notes, and snippets.

@beccasaurus
Created May 5, 2011 19:37
Show Gist options
  • Save beccasaurus/957732 to your computer and use it in GitHub Desktop.
Save beccasaurus/957732 to your computer and use it in GitHub Desktop.
Adds hooks to jQuery.validate's form/element validation methods (via trigger())

jQuery Validate Hooks

If you're using ASP.NET MVC3, it uses jQuery Validate to do client-side validations. Instead of using jQuery Validate directly, however, it wraps it with its own jQuery plugin called jQuery.Validate.Unobtrusive. jQuery.Validate.Unobtrusive sets up jQuery Validate for you, behind the scenes, so you don't have an opportunity to customize your jQuery Validate settings at all!

We've been running into trouble with this when we've been doing our own custom client-side validations. We need a way to integrate with the build-in ASP.NET MVC3 validation so we can:

  • Find out when our form's client-side validation runs and do something custom, if it fails
  • Find out when a particular element's client-side validation runs and do something custom if it passes or fails

We might extend this to be able to hook into other things, like jQuery Validate's submitHandler (which allows you to override what happens when the form is valid and ready to be submitted).

Installation

<script type="text/javascript" src="jquery.validate.hooks.js"></script>

Usage

$(document).ready(function(){

  // we're assuming that this has already been run (which MVC3 does automatically)
  // $('form').validate();

  // running this overrides some jQuery Validate stuff so we can hook into its validations.
  // triggerElementValidationsOnFormValidation is optional and will fire off all of your 
  // element validations WHEN the form validation runs ... it requires jquery.validate.unobtrusive
  $('form').addTriggersToJqueryValidate().triggerElementValidationsOnFormValidation();

  // You can bind to events that the forms/elements trigger on validation
  $('form').bind('formValidation', function(event, element, result) {
    console.log(['validation ran for form:', element, 'and the result was:', result]);
  });

  // Or you can use the helper functions that we created for binding to these events
  $('form').formValidation(function(element, result) {
    console.log(['validation ran for form:', element, 'and the result was:', result]);
  });

  $('input.something').elementValidation(function(element, result) {
    console.log(['validation ran for element:', element, 'and the result was:', result]);
  });

  $('input#address').elementValidationSuccess(function(element) {
    console.log(['validations just ran for this element and it was valid!', element]);
  });

  $('input#address').elementValidAndInvalid(function(element) {
    console.log(['validations just ran for this element and it was valid!', element]);
  }, function(element){
    console.log(['validations just ran for this element and it was INVALID!', element]);
  });
});
(function($) {
$.fn.addTriggersToJqueryValidate = function() {
// Loop thru the elements that we jQuery validate is attached to
// and return the loop, so jQuery function chaining will work.
return this.each(function(){
var form = $(this);
// Grab this element's validator object (if it has one)
var validator = form.data('validator');
// Only run this code if there's a validator associated with this element
if (! validator)
return;
// Only add these triggers to each element once
if (form.data('jQueryValidateTriggersAdded'))
return;
else
form.data('jQueryValidateTriggersAdded', true);
// Override the function that validates the whole form to trigger a
// formValidation event and either formValidationSuccess or formValidationError
var oldForm = validator.form;
validator.form = function() {
var result = oldForm.apply(this, arguments);
var form = this.currentForm;
$(form).trigger((result == true) ? 'formValidationSuccess' : 'formValidationError', form);
$(form).trigger('formValidation', [form, result]);
return result;
};
// Override the function that validates the whole element to trigger a
// elementValidation event and either elementValidationSuccess or elementValidationError
var oldElement = validator.element;
validator.element = function(element) {
var result = oldElement.apply(this, arguments);
$(element).trigger((result == true) ? 'elementValidationSuccess' : 'elementValidationError', element);
$(element).trigger('elementValidation', [element, result]);
return result;
};
});
};
/* Below here are helper methods for calling .bind() for you */
$.fn.extend({
// Wouldn't it be nice if, when the full form's validation runs, it triggers the
// element* validation events? Well, that's what this does!
//
// NOTE: This is VERY coupled with jquery.validation.unobtrusive and uses its
// element attributes to figure out which fields use validation and
// whether or not they're currently valid.
//
triggerElementValidationsOnFormValidation: function() {
return this.each(function(){
$(this).bind('formValidation', function(e, form, result){
$(form).find('*[data-val=true]').each(function(i, field){
if ($(field).hasClass('input-validation-error')) {
$(field).trigger('elementValidationError', field);
$(field).trigger('elementValidation', [field, false]);
} else {
$(field).trigger('elementValidationSuccess', field);
$(field).trigger('elementValidation', [field, true]);
}
});
});
});
},
formValidation: function(fn) {
return this.each(function(){
$(this).bind('formValidation', function(e, element, result){ fn(element, result); });
});
},
formValidationSuccess: function(fn) {
return this.each(function(){
$(this).bind('formValidationSuccess', function(e, element){ fn(element); });
});
},
formValidationError: function(fn) {
return this.each(function(){
$(this).bind('formValidationError', function(e, element){ fn(element); });
});
},
formValidAndInvalid: function(valid, invalid) {
return this.each(function(){
$(this).bind('formValidationSuccess', function(e, element){ valid(element); });
$(this).bind('formValidationError', function(e, element){ invalid(element); });
});
},
elementValidation: function(fn) {
return this.each(function(){
$(this).bind('elementValidation', function(e, element, result){ fn(element, result); });
});
},
elementValidationSuccess: function(fn) {
return this.each(function(){
$(this).bind('elementValidationSuccess', function(e, element){ fn(element); });
});
},
elementValidationError: function(fn) {
return this.each(function(){
$(this).bind('elementValidationError', function(e, element){ fn(element); });
});
},
elementValidAndInvalid: function(valid, invalid) {
return this.each(function(){
$(this).bind('elementValidationSuccess', function(e, element){ valid(element); });
$(this).bind('elementValidationError', function(e, element){ invalid(element); });
});
}
});
})(jQuery);
@rzaks
Copy link

rzaks commented Jan 13, 2015

This is fantastic! Saved me a lot of time!

@migig
Copy link

migig commented Apr 4, 2015

@remi Please add to this to a github repo, with a MIT license? And then publish to bower?

This code is gold, deserves to be a full library not just a gist!

@migig
Copy link

migig commented May 21, 2015

@remi "We might extend this to be able to hook into other things, like jQuery Validate's submitHandler".

How do you do that? We've been doing it like this:

var oldSubmitHandler = validator.settings.submitHandler;
validator.settings.submitHandler = function () {
  var form = this.currentForm;
  $(form).trigger("formSubmitted", form);
  return oldSubmitHandler
    ? oldSubmitHandler.apply(this, arguments)
    : true;
};

@Yorie
Copy link

Yorie commented Sep 19, 2015

Very strange event firing order, really. Why "formValidatiom" is triggered before "formValidationError" and "formValidationSuccess". But in your example you use word "ran" that is in past time.
So, "formValidation" should be called "afterValidation". May be I missing documentation page, but event names are not clear to understand.
So, as result here is no "beforeValidation" event, that is very disappointing :(
Before validation event would be usefull if I want to make some actions before validating the form, e.g. displaying spinners while executing ajax resuests, etc.

@BernieCook
Copy link

Great work mate, very helpful indeed.

@davidoneal
Copy link

I've included jquery.validate.hooks.js in my bundle, but when I call $('form').addTriggersToJqueryValidate().triggerElementValidationsOnFormValidation();
I still get " Object doesn't support property or method 'addTriggersToJqueryValidate'"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment