50

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

Sparky
  • 98,165
  • 25
  • 199
  • 285
WooWaaBob
  • 3,397
  • 3
  • 23
  • 23
  • anybody: is there any official documentation for this from MS ? – Simon_Weaver Dec 23 '12 at 08:35
  • note: the `handleSubmit()` is called only if all form values validate. It is not called if you have any errors. It's perfect for asking for confirmation like you are (because you know everything is valid) but if you want special handling when there are errors you must use `invalidHandler` - see DGreen's answer – Simon_Weaver Dec 23 '12 at 21:00
  • Rather than waiting until it's already initialized, you can also call [`jQuery.validator.setDefaults`](https://jqueryvalidation.org/jQuery.validator.setDefaults/) directly and any subsequent form intializations will use those settings. Just make sure it runs before the DOM is loaded and jQuery Validator automatically initializes the forms. – KyleMit Sep 28 '16 at 03:11

5 Answers5

79

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(); };
petrhaus
  • 1,319
  • 12
  • 12
  • 2
    Thanks @petrhaus - nice simple answer that has also increased my understanding of jQuery. – WooWaaBob Jan 24 '11 at 10:17
  • 5
    For dynamically loaded forms (ex. dialog), you must first call $.validator.unobtrusive.parse($('form')) or $('form').data will return undefined. – parliament Feb 18 '13 at 04:49
  • Frustrating part about this is that i was stuck trying to figure out what was going wrong because if the submitHandler was set in – Seravy Feb 06 '14 at 08:42
  • I tried all of the above to handle the `invalidHandler` popping an `alert`, but it doesn't pop up. And `$('form').data('validator')` returns `undefined`, even after I tried calling `$.validator.unobtrusive.parse($('form'))` as @parliament suggested. – Shimmy Weitzhandler Apr 25 '15 at 23:25
  • I've added [this](http://stackoverflow.com/questions/29872082/trigger-an-event-if-there-are-validation-errors) new question. – Shimmy Weitzhandler Apr 25 '15 at 23:58
  • 1
    **NB** Two caveats with this. (1) If using AJAX forms it will not work (see [here](http://stackoverflow.com/a/1305766/67392): `$(theForm).trigger("sumbit")`). (2) need to avoid calling `submit` on the jQuery wrapper: it will just call validation and the handler. Of course #1 causes #2! Only way seems to be explicitly setting `submitHandler` to `null` the first time around. – Richard Jun 23 '15 at 09:29
  • Also, I use `.validate()` function to obtain the validator object instead of `.data("validator")` in case the library changes its implementation. So my code would be: `$("form").validate().settings.submitHandler = function..` – timmi4sa Sep 05 '19 at 23:40
68

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.

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
DGreen
  • 1,027
  • 1
  • 8
  • 17
38

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.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • 4
    Nice detailed explanation Simon especially compared to my vague answer :) – DGreen Feb 01 '13 at 14:53
  • 3
    @DGreen my answers tend to be proportional to the amount of pain and time the problem has caused me - but without your answer it would have been a lot longer by the time I'd figured it out! – Simon_Weaver Jul 13 '13 at 10:24
  • I tried all of the above to handle the `invalidHandler` popping an `alert`, but it doesn't pop up. And `$('form').data('validator')` returns `undefined`. – Shimmy Weitzhandler Apr 25 '15 at 23:17
  • It's probably changed a lot since this answer :-( – Simon_Weaver Apr 25 '15 at 23:24
  • @Simon_Weaver, thanks for your response. I now see that it's actually working, but only if I try to submit the form. Is there an event that's raised when there are validation errors whatsoever, even when the validation errors were added by the server during a previous postback? – Shimmy Weitzhandler Apr 25 '15 at 23:41
  • @Simon_Weaver I've added [this](http://stackoverflow.com/questions/29872082/trigger-an-event-if-there-are-validation-errors) new question. – Shimmy Weitzhandler Apr 25 '15 at 23:57
12

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?");
});
Tracker1
  • 19,103
  • 12
  • 80
  • 106
1

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;
    }
}
Andrew
  • 8,322
  • 2
  • 47
  • 70