How to add a 'submitHandler' function when using jQuery Unobtrusive Validation?
I'm validating a form using the new unobtrusive validation features in ASP.NET MVC 3.
So there is no code that I have written to setup jQuery validate to start validating my form. Its all done by loading the jQuery.validate.unobtrusive.js library.
Unfortunately I need to whack in a 'are you sure?' message box when the form is valid but before submitting. With jQuery validate you would add the option handleSubmit when initialising like so:
$("#my_form").validate({
rules: {
field1: "required",
field1: {email: true },
field2: "required"
},
submitHandler: function(form) {
if(confirm('Are you sure?')) {
form.submit();
}
}
})开发者_开发问答;
But you don't need to initialise when using the unobtrusive library.
Any ideas where/how I can add a submit handler in that case?
Thx
The unobtrusive library will attach the validator on all forms, so you have to replace the submitHandler this way:
$("form").data("validator").settings.submitHandler = function (form) { alert('submit'); form.submit(); };
I found this question while looking for a way to set the invalidHandler
, as opposed to the submitHandler
. Interestingly, you can't set invalidHandler
in the same way when using unobtrusive validation.
The function is not fired when an invalid form is detected:
$("form").data("validator").settings.invalidHandler = function (form, validator) {
alert('invalid!');
};
This approach works:
$("form").bind("invalid-form.validate", function () {
alert('invalid!');
});
It seems to be due to the way that the jquery.validate.unobtrusive.js script initialises the Validate plugin, and the way that the plugin invokes the handler.
The following findings may be of interest to understand the difference between :
submitHandler : Called when the form is validated successfully - right before the server postback
invalidHandler : Called if validation fails and there is no post back
@DGreen's solution definitely seems to be the best way to inject special handling if the form validation fails and you need to do something such as display a pop-up validation summary (invalidHandler)
$("form").bind("invalid-form.validate", function () {
alert('invalid!');
});
@PeteHaus solution is the best way to do something if you want to prevent postback of a successfully validated form (submitHandler)
$("form").data("validator").settings.submitHandler = function (form) {
alert('submit'); form.submit(); };
However I was slightly concerned about what behavior I was overriding (or not overriding) by binding to the .validate
method like this - and why I had to do each way differently. So I checked the source. I strongly recommend anybody who wants to understand this procedure more does so too - put in some 'alert' statements or 'debugger' statements and it's pretty easy to follow along*
Anyway it turns out that when the jquery.validate.unobtrusive
handler initializes jquery.validate
plugin it does so in the parseElement()
method by retrieving options created by the validationInfo()
method.
ValidationInfo()
returns the options like this:
options: { // options structure passed to jQuery Validate's validate() method
errorClass: "input-validation-error",
errorElement: "span",
errorPlacement: $.proxy(onError, form),
invalidHandler: $.proxy(onErrors, form),
messages: {},
rules: {},
success: $.proxy(onSuccess, form)
},
The onErrors()
method in jquery.validate.unobtrusive
is responsible for dynamically creating the validation summary panel for MVC. If you're not creating a validation summary panel (with @Html.ValidationSummary()
which must incidentally be contained within the FORM body) then this method is completely inert and does nothing so you don't need to worry.
function onErrors(event, validator) { // 'this' is the form elementz
var container = $(this).find("[data-valmsg-summary=true]"),
list = container.find("ul");
if (list && list.length && validator.errorList.length) {
list.empty();
container.addClass("validation-summary-errors").removeClass("validation-summary-valid");
$.each(validator.errorList, function () {
$("<li />").html(this.message).appendTo(list);
});
}
}
If you really want to you can unbind the jquery.validate.unobtrusive
handler like this - but i wouldn't bother myself
$("form").unbind("invalid-form.validate"); // unbind default MS handler
If you're wondering why this works
$("form").data("validator").settings.submitHandler = ...
and this doesn't work
$("form").data("validator").settings.invalidHandler = ...
it's because submitHandler
is explicily called inside jquery.validate
when validation is performed. Therefore it doesn't matter at what point it is set.
validator.settings.submitHandler.call( validator, validator.currentForm );
but the invalidHandler
is bound to an event during init()
like this so if you set it in your own code it is too late
if (this.settings.invalidHandler)
$(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
A little stepping through code makes for a lot of understanding sometimes! Hope this helps
*Make sure you don't have minification in bundles enabled.
I prefer to inject an event handler, so that it can be bound to... :)
//somewhere in your global scripts
$("form").data("validator").settings.submitHandler = function (form) {
var ret = $(form).trigger('before-submit');
if (ret !== false) form.submit();
};
This way, I can bind to it anywhere needed...
//in your view script(s)
$("form").bind("before-submit", function() {
return confirm("Are you sure?");
});
I based this on the original answer, which didn't work for me. I guess things have changed in jQuery.
As usual, take this code and add copious error checking :D I use similar code to prevent double-submits on our forms, and it works fine in IE8+ (as of mvc4 and jquery 1.8.x).
$("#myFormId").confirmAfterValidation();
// placed outside of a $(function(){ scope
jQuery.fn.confirmAfterValidation = function () {
// do error checking first. should be null if no handler already
$(this).data('validator').settings.submitHandler = confirmValidForm;
};
function confirmValidForm(form) {
var validator = $(this);
var f = $(form);
if (confirm("really really submit?")) {
// unbind so our handler doesn't get called again, and submit
f.unbind('submit').submit();
}
// return value is _ignored_ by jquery.validate, so we have to set
// the flag on the validator itself, but it should cancel the submit
// anyway if we have a submitHandler
validator.cancelSubmit = true;
}
}
精彩评论