103

I am working with ASP.Net MVC3, the easier way to use the client validation would be enabling the jquery.validate.unobtrusive. Everything works fine, for stuff that's right from server.

But when I try to inject some new 'inputs' with javascript, and I knew that I need to call $.validator.unobtrusive.parse() to rebind the validations. But still, all those dynamic injected fields are not functioning.

Even worse, I try to manually bind using jquery.validate and it is not working either. Any thoughts?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
xandy
  • 27,357
  • 8
  • 59
  • 64
  • 2
    If you came here from the google you're probably looking for @Sundara Prabu's answer as the other ones that are massively upvoted are old. – The Muffin Man Nov 25 '15 at 18:51

13 Answers13

170

I tried Xhalent's approach but unfortunately it wasn't working for me. Robin's approach did work and didn't work. It worked great for dynamically added elements, but if you tried to use JQuery to remove all the validation attributes and spans from the DOM, the validation library still would try to validate them.

However, if you remove the form's "unobtrusiveValidation" data in addition to "validationData", it worked like a charm for dynamically adding and removing elements that you want validated or not validated.

$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
viggity
  • 15,039
  • 7
  • 88
  • 96
  • 2
    Xhalent's didn't work for me at first either, then i noticed that parse should be passed a container for the new elements, NOT the elements themselves - quick fix would be to pass the whole form instead of the element(s) - then it worked like a charm. – Per Hornshøj-Schierbeck May 09 '11 at 14:02
  • 1
    any idea why it says `validator` is not defined? I have both validation and validation.unobtrusive referenced. Validation works until I call these code – Shawn Mclean Aug 08 '11 at 18:16
  • 4
    Thanks. It works for content added to page in ASP.MVC partial views added via AJAX call. – Maksym Kozlenko Jul 09 '12 at 02:02
  • Thanks this helped. But I am facing an issue where I am using accordions on the page and if the accordion is collapsed, it does't fire the validations. How can I fix this? I know if I had gone for normal jquery validate plugin instead of MVC3 unobtrusive, I had to say `$('#form').validate({ignore: ""})` – parsh Jun 07 '13 at 18:29
  • You need to make sure your clear up any events added to the form by the previous validator, otherwise you will have eventhandlers from the previous validator running as well. Unfortunatelty jQuery validation doesn't namespace its handlers :(. You can monkey patch $.fn.validateDelegate to add a namespace (ie '.validate') then use `$('form').unbind('.validate') after removing the data values but before re-parsing – Robert Slaney Oct 30 '14 at 22:20
  • 1
    It worked for me in PHP. just adding this info because every comment points to .NET MVC. :P – finnTheHumin Nov 17 '14 at 08:21
  • @RobertSlaney - This sounds like an important point that is not usually addressed. Can you provide a more detailed example of your proposed solution? – xr280xr Jun 18 '15 at 21:41
  • @Viggity, But this method clear my original validation messages. Any idea how to keep that? – Dhwani Oct 20 '15 at 09:05
  • You should also say `$("form").off(".validate")` which makes sure the events are removed. Alternatively you could call `$("form").validate().destroy()` which does this aswell as remove the data. – nfplee Jun 12 '20 at 20:30
65

I've created an extension for the jquery.validate.unobtrusive library that solved this problem for my situation - it might be of interest.

http://xhalent.wordpress.com/2011/01/24/applying-unobtrusive-validation-to-dynamic-content/

Xhalent
  • 3,914
  • 22
  • 21
  • I think I should vote your answer as the right one. BTW, is your library works with the latest MVC RC? or they just fixed that? – xandy Jan 24 '11 at 10:03
  • I'm using it with the MVC 3.0 RTM – Xhalent Jan 24 '11 at 20:48
  • I Spent a full day on this problem, its 2:03 AM and i found this solution, works! thanks!!! – Shrage Smilowitz Jan 26 '11 at 07:03
  • 1
    When using the code linked above, make sure you update it to include the code in the comments otherwise it can break. Specifically, change the two selector lines to double quote the id. – Ryan O'Neill Jul 14 '11 at 19:50
  • @xhalent, how does this work with dynamically removing a field from validation? I'm not removing from the DOM, but actually hiding it and changing the url of the form action – Shawn Mclean Aug 07 '11 at 15:06
  • Also when using above code make sure to move the form outside of the dynamic content, the form needs to be static – Anders Oct 10 '11 at 09:09
  • I thought this worked at first, but then I noticed that it only work on the rows that were added in the first call, I the next ajax response are more rows it only validates the rows that were in the first response. How can I get it to validate all rows in the ajax response? Thanks! – Anders Oct 10 '11 at 12:42
  • Ok, got it to work with my scenario (A table with input elements on each row).. Have the form in inside the dynamic content so that its loaded with each ajax call, and then call $.validator.unobtrusive.parse("-your-selector-"); – Anders Oct 10 '11 at 13:30
  • 1
    Was finally able to get this working for my scenario after some debugging. It appears shouldn't pass in an input selector, but the input's parent or the form, due to the call to `$.validator.unobtrusive.parse()`. Also, it appears to only add new rules, but not pick up updates to existing rules? – lambinator Oct 27 '11 at 18:46
  • I have the problem of where If I add data-val-* attributes to elements and then call parseDyanamicContent it will work as expected. But when I remove those data-val-* attributes and call parseDyanamicContent again.. it doesnt remove the validations. It would be nice if someone could come up with a solution to this problem and Steve Lambs. – Evan Larsen May 17 '12 at 20:27
  • Great solution. Just one note - it's better to change `$("[name='" + elname + "']").rules("add", args);` to `form.find("[name='" + elname + "']").rules("add", args);` in your library. The reason is that if there're two forms that contain elements with the same names the validation does not work correctly for the second form. That was my case :) – mayor Nov 12 '13 at 21:47
  • Great solution. Fixed a problem where I first removed the validator from the form and causing jQuery validator to access properties of it before I could recreate it after replacing part of the form with dynamic content. – Christophe Geers Nov 26 '13 at 13:26
  • 6
    This answer should contain at least the bear bones of the blog post. Link only answers are generally off topic for SO – Liam Mar 19 '14 at 16:49
  • 9
    While this answer is 3+ years old it would be extremely helpful if you could post the essential parts of the answer here, on this site, or your post risks being deleted [See the FAQ where it mentions answers that are 'barely more than a link'.](http://stackoverflow.com/faq#deletion) You may still include the link if you wish, but only as a 'reference'. The answer should stand on its own without needing the link. – Taryn Mar 28 '14 at 13:33
45

I actually really like the simplicity of @viggity and @Robins solution, so I turned it into a quick little plugin:

(function ($) {

    $.fn.updateValidation = function () {
        var $this = $(this);
        var form = $this.closest("form")
            .removeData("validator")
            .removeData("unobtrusiveValidation");

        $.validator.unobtrusive.parse(form);

        return $this;
    };
})(jQuery);

Example usage:

$("#DischargeOutcomeNumberOfVisits")
    .attr("data-val-range-min", this.checked ? "1" : "2")
    .updateValidation();
Peter Kiss
  • 9,309
  • 2
  • 23
  • 38
lambinator
  • 10,616
  • 7
  • 55
  • 57
34

I'm having the same problem. I discovered it's not possible to call $.validator.unobtrusive.parse() on the same form twice. When loading the form initially from the server the form is parsed automatically by the unobtrusive library. When you add an input element dynamically to the form and call $.validator.unobtrusive.parse() again, it won't work. The same goes for parseElement().

The unobtrusive lib calls the validate method of the jquery validate plugin to set all the rules and messages. Problem is, when called again, the plugin doesn't update the new set of rules its given.

I found one crude solution: Before calling the parse method on the unobstrusive lib, i throw away the form validator:

$('yourForm').removeData("validator");

Now, when the validate method is called by the unobtrusive lib, all rules and messages are recreated including the dynamically added inputs.

Hope this helps

Robin van der Knaap
  • 4,060
  • 2
  • 33
  • 48
  • Deserves a +1 - it's a crude solution like you said yourself, but it's pretty clear what it does and what state you're left in afterwards – Per Hornshøj-Schierbeck Feb 03 '11 at 08:19
  • 2
    From Brad Wilsons blog: "You can also call the jQuery.validator.unobtrusive.parseElement() function to parse a single HTML element." Any help? – jamesfm Feb 15 '11 at 19:32
  • @Per Hornshøj-Schierbeck: @Robin van der Knaap: I've spent the last 2-3 hours to figure this out!! Anyway- this worked WONDERFULLY. Crude makes me feel I shouldn't use it though- why would I not want to do this? – KTF May 05 '11 at 22:28
  • 2
    The solution is a bit crude because all validators are removed and reinstated again, including the dynamically added validator. It would be better if only the dynamically added validator was updated. @Xhalent provides a cleaner solution in his answer, but basically the unobtrusive library from MS should be updated for this scenario. – Robin van der Knaap May 07 '11 at 16:10
10

I am using MVC 4 and JQuery 1.8, looks like the following piece of code is needed to enable Jquery to validation dynamically injected content via Ajax, or Jquery into the DOM.

I have made a modular function which accepts the Jquery object of the newly added element. If you have cloned a new table with id tblContacts using Jquery on click of a button, then include the function below in your js file

function fnValidateDynamicContent(element) {
    var currForm = element.closest("form");
    currForm.removeData("validator");
    currForm.removeData("unobtrusiveValidation");
    $.validator.unobtrusive.parse(currForm);
    currForm.validate(); // This line is important and added for client side validation to trigger, without this it didn't fire client side errors.
}

and call it like this:

fnValidateDynamicContent("#tblContacts")
KyleMit
  • 30,350
  • 66
  • 462
  • 664
Sundara Prabu
  • 2,361
  • 1
  • 21
  • 20
9

I tried viggity's answer and at first everything seemed to work. But after a while i noticed that the validation becomes painfully slow the more dynamically items I added. The reason was that his solution doesn't unbind the event handlers, but add new ones each time. So if you add 5 items the validation is executed 6 times instead of only once. To fix this you have to unbind the events additionally to the removeData calls.

$("form").removeData("validator")
         .removeData("unobtrusiveValidation")
         .off("submit.validate click.validate focusin.validate focusout.validate keyup.validate invalid-form.validate");
$.validator.unobtrusive.parse("form");
Benedikt Langer
  • 417
  • 4
  • 11
  • This is simply brilliant, and no one is paying attention to it because it's simply hard to see. This answer should have more upvotes. – sTx Nov 16 '21 at 16:49
4

Why not use the rules function directly from jquery validation doc. Like this:

$('#newField0').rules('add', {
    required: true,
    minlength: 2
});
//use Html.ValidationMessage will renders a span element, unobtrusive need it to display errors
$('@Html.ValidationMessage("newField0")').insertAfter('#newField0');
zhangfx
  • 153
  • 1
  • 5
2

Taking from Xhalent's solution marked as answer above, I expanded on it a bit.

$.validator.unobtrusive.parseDynamicContent = function (selector) {
    var $selector = $(selector),
        $jqValUnob = $.validator.unobtrusive,
        selectorsDataValAttr = $selector.attr('data-val'),
        $validationInputs = $selector.find(':input[data-val=true]');

    if ((selectorsDataValAttr !== 'true') && 
        ($validationInputs.length === 0)) { 
        return; 
    }

    if (selectorsDataValAttr === 'true') {
        $jqValUnob.parseElement(selector, true);
    }

    $validationInputs.each(function () {
        $jqValUnob.parseElement(this, true);
    });

    //get the relevant form
    var $form = $selector.first().closest('form');

    $jqValUnob.syncValdators($form);
};

/* synchronizes the unobtrusive validation with jquery validator */
$.validator.unobtrusive.syncValdators = function ($form) {
    if ($.hasData($form[0])) {
        var unobtrusiveValidation = $form.data('unobtrusiveValidation'),
            validator = $form.validate();

        // add validation rules from unobtrusive to jquery
        $.each(unobtrusiveValidation.options.rules, function (elname, elrules) {
            if (validator.settings.rules[elname] == undefined) {
                var args = {};
                $.extend(args, elrules);
                args.messages = unobtrusiveValidation.options.messages[elname];
                $("[name='" + elname + "']").rules("add", args);
            } else {
                $.each(elrules, function (rulename, data) {
                    if (validator.settings.rules[elname][rulename] == undefined) {
                        var args = {};
                        args[rulename] = data;
                        args.messages = unobtrusiveValidation.options.messages[elname][rulename];
                        $("[name='" + elname + "']").rules("add", args);
                    }
                });
            }
        });
        // remove all validation rules from jquery that arn't in unobtrusive
        $.each(validator.settings.rules, function (elname, elrules) {
            if (unobtrusiveValidation.options.rules[elname] === undefined) {
                delete validator.settings.rules[elname];
            } else {
                $.each(elrules, function (rulename, data) {
                    if (rulename !== "messages" && unobtrusiveValidation.options.rules[elname][rulename] === undefined) {
                        delete validator.settings.rules[elname][rulename];
                    }
                });
            }
        });
    }        
};

$.validator.unobtrusive.unparseContent = function (selector) {
    var $selector = $(selector);

    // if its  a text node, then exit
    if ($selector && $selector.length > 0 && $selector[0].nodeType === 3) {
        return;
    }

    var $form = $selector.first().closest('form'), 
        unobtrusiveValidation = $form.data('unobtrusiveValidation');

    $selector.find(":input[data-val=true]").each(function () {
        removeValidation($(this), unobtrusiveValidation);
    });
    if ($selector.attr('data-val') === 'true') {
        removeValidation($selector, unobtrusiveValidation);
    }
    $.validator.unobtrusive.syncValdators($form);
};

function removeValidation($element, unobtrusiveValidation) {
    var elname = $element.attr('name');
    if (elname !== undefined) {
        $element.rules('remove');
        if (unobtrusiveValidation) {
            if (unobtrusiveValidation.options.rules[elname]) {
                delete unobtrusiveValidation.options.rules[elname];
            }
            if (unobtrusiveValidation.options.messages[elname]) {
                delete unobtrusiveValidation.options.messages[elname];
            }
        }
    }
}

So basically it still works the same as Xhalent's solution above but I added the ability to remove rules for elements you remove from the dom. So, when you remove elements from the Dom and you want those validation rules removed also then call:

$.validator.unobtrusive.unparseContent('input.something');
Evan Larsen
  • 9,935
  • 4
  • 46
  • 60
1

I have been fiddling around with this for a while, scrapping solutions and trying again later (when I had some spare time, believe it or not).

I am not sure if this behaviour would have changed in newer versions of jquery (we're using 1.7.2) since this thread was created or commented last, but I found that .parseElement(inputElement) works fine when I try to add dynamically created elements to a form that already has a validator loaded. This was already suggested by @jamesfm (Feb 15 '11) in one of the comments above, but I overlooked it the first few times I was working on this. So I'm adding it as a separate answer to make it more obvious and because I think it is a good solution and doesn't require so much overhead. It might not be relevant for all the issues raised in subsequent answers but I think it would be a solution to the original question. Here's how I got mine working:

//row & textarea created dynamically by an async ajax call further up in the code
var row = ...; //row reference from somewhere
var textarea = row.find("textarea");
textarea.val("[someValue]");

//add max length rule to the text area
textarea.rules('add', {
    maxlength: 2000
});
//parse the element to enable the validation on the dynamically added element
$.validator.unobtrusive.parseElement(textarea);
Ben
  • 1,537
  • 1
  • 17
  • 20
1

I found @Xhalent's code script in my code and was going to delete it because I was not using it, which lead me to this SO question.

This code is pretty clean and simple:

jQuery.fn.unobtrusiveValidationForceBind = function () {
    //If you try to parse a form that is already parsed it won't update
    var $form = this
       .removeData("validator") /* added by the raw jquery.validate plugin */
            .removeData("unobtrusiveValidation");  /* added by the jquery     unobtrusive plugin */

    $form.bindUnobtrusiveValidation();
}

Then, to call this jQuery extension, just use a selector to grab you form:

$('#formStart').unobtrusiveValidationForceBind();

Viola!

Brian Ogden
  • 18,439
  • 10
  • 97
  • 176
1

In case of dynamic contents you need to update Unobtrusive Validations as below and check if Form is valid while submitting.

function UpdateUnobtrusiveValidations(idForm) {
    $(idForm).removeData("validator").removeData("unobtrusiveValidation");
    $.validator.unobtrusive.parse($(idForm));
};


$('#idDivPage').on('click', '#idSave', function (e) {
    e.preventDefault();
    if (!$('#idForm').valid()) {
        // Form is invalid … so return
        return false;
    }
    else {
        // post data to server using ajax call
        // update Unobtrusive Validations again
        UpdateUnobtrusiveValidations('#idForm');
    }
});
KyleMit
  • 30,350
  • 66
  • 462
  • 664
0

Firstly, I think the call should be to .validator, not validate then you need to pass in the id of the form

$.validator.unobtrusive.parse("#id");
Irvin Dominin
  • 30,819
  • 9
  • 77
  • 111
  • I assume you've loaded both the jquery.validate & jquery.validate.unobtrusive scripts and called the Html.EnableClientValidation() and Html.EnableUnobtrusiveJavaScript() methods? Can you post a code sample for your base page and the dynamically loaded form perhaps? – Chris Allmark Dec 21 '10 at 15:58
0

If you need to add and remove things with the possibility that some errors are already displayed, this is what I came with. It is based mainly on different ideas on this question page. I use the built in destroy() instead of just removing the data attribute for JQuery validate.

function resetValidator($form: JQuery) {
    $form.validate().destroy();

    //reset unobtrusive validation summary, if it exists
    $form.find("[data-valmsg-summary=true]")
        .removeClass("validation-summary-errors")
        .addClass("validation-summary-valid")
        .find("ul").empty();

    //reset unobtrusive field level, if it exists
    $form.find("[data-valmsg-replace]")
        .removeClass("field-validation-error")
        .addClass("field-validation-valid")
        .empty();

    $form.removeData("unobtrusiveValidation");
}

You call it before dynamically modifying the inputs in the form. You then reinitialize the validation after.

resetValidator($form);
//add or remove inputs here ...
$.validator.unobtrusive.parse($form);

Note: If some or all inputs have been validated and have errors, those errors will be reset... but they will correctly come back on the next validation.

Yepeekai
  • 2,545
  • 29
  • 22