jquery.validate.unobtrusive not working with dynamic injected elements
Asked Answered
A

13

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?

Adal answered 10/12, 2010 at 6:27 Comment(1)
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.Shinar
L
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/

Layne answered 24/1, 2011 at 1:29 Comment(14)
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?Adal
I'm using it with the MVC 3.0 RTMLayne
I Spent a full day on this problem, its 2:03 AM and i found this solution, works! thanks!!!Cageling
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.Swanherd
@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 actionKenward
Also when using above code make sure to move the form outside of the dynamic content, the form needs to be staticStarcrossed
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!Starcrossed
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-");Starcrossed
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?Footlights
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.Monospermous
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 :)Motherhood
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.Breakout
This answer should contain at least the bear bones of the blog post. Link only answers are generally off topic for SOExfoliation
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'. 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.Vestigial
G
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");
Gloria answered 25/4, 2011 at 20:41 Comment(9)
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.Sibilate
any idea why it says validator is not defined? I have both validation and validation.unobtrusive referenced. Validation works until I call these codeKenward
Thanks. It works for content added to page in ASP.MVC partial views added via AJAX call.Audubon
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: ""})Groundmass
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-parsingWon
It worked for me in PHP. just adding this info because every comment points to .NET MVC. :PAvocet
@RobertSlaney - This sounds like an important point that is not usually addressed. Can you provide a more detailed example of your proposed solution?Dissatisfactory
@Viggity, But this method clear my original validation messages. Any idea how to keep that?Anatomy
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.Twinned
L
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/

Layne answered 24/1, 2011 at 1:29 Comment(14)
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?Adal
I'm using it with the MVC 3.0 RTMLayne
I Spent a full day on this problem, its 2:03 AM and i found this solution, works! thanks!!!Cageling
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.Swanherd
@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 actionKenward
Also when using above code make sure to move the form outside of the dynamic content, the form needs to be staticStarcrossed
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!Starcrossed
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-");Starcrossed
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?Footlights
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.Monospermous
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 :)Motherhood
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.Breakout
This answer should contain at least the bear bones of the blog post. Link only answers are generally off topic for SOExfoliation
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'. 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.Vestigial
F
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();
Footlights answered 27/10, 2011 at 2:45 Comment(4)
replace $.validator.unobtrusive.parse("#" + form.attr("id")); with $.validator.unobtrusive.parse(form);Chauchaucer
you can also safely remove .first() as closest returns only 1 element.Chauchaucer
Nice, I like this simple approachBrightwork
Works beautifully for both adding and removing data-val attributesLarceny
O
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

Oftentimes answered 18/1, 2011 at 0:9 Comment(4)
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 afterwardsSibilate
From Brad Wilsons blog: "You can also call the jQuery.validator.unobtrusive.parseElement() function to parse a single HTML element." Any help?Clinic
@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?Pricking
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. @Layne provides a cleaner solution in his answer, but basically the unobtrusive library from MS should be updated for this scenario.Oftentimes
S
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")
Sura answered 28/2, 2013 at 6:51 Comment(3)
currform.validate(); should actually be currForm.validate(); (typo). Thanks for sharing though, this worked for meColver
@JohnMc fixed the typo nowSura
This is the only one that worked for me (mvc 4 and jquery 1.10)Shinar
Z
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");
Zach answered 10/2, 2016 at 8:51 Comment(1)
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.Brindle
M
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');
Margemargeaux answered 7/3, 2014 at 8:45 Comment(0)
M
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');
Monospermous answered 21/5, 2012 at 15:24 Comment(0)
C
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);
Calamite answered 19/3, 2013 at 0:47 Comment(0)
M
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!

Maximalist answered 10/10, 2015 at 3:56 Comment(0)
F
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');
    }
});
Faefaeces answered 28/10, 2015 at 5:56 Comment(0)
C
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");
Costrel answered 21/12, 2010 at 8:40 Comment(1)
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?Costrel
R
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.

Rebecarebecca answered 11/6, 2020 at 14:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.