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);
@samueljseay
Copy link

Thank you so much for these hooks. Works perfectly for me! Wonderful! Perhaps you could put some Author/Copyright stuff in, so credit goes where it is due.

@rico
Copy link

rico commented Nov 16, 2012

This is wonderful! Should definitely find its way into the plugin.

@raisr
Copy link

raisr commented Feb 20, 2013

This saved my day. I've found one little point of improvement. If i use the Remote validation attribute for a field the unobtrusive validation runs while entering text (the error is displayed immediatly), but your hooks works only after leaving the field. So i see the error message while typing my text and your callbacks are called after leaving the field.

@agzam
Copy link

agzam commented Apr 11, 2013

this thing is incredible. Although I can't find how to mark an element as 'invalid' because there's no 'beforeValidation' function

@bahmanworld
Copy link

Great!!!

@hogart
Copy link

hogart commented Jun 23, 2014

I was thinking of using this code for one of my projects, and was wondering what license was needed. Like maybe MIT?

@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