无法使用动态注入的元素

我使用的是 ASP.Net MVC3,使用客户端验证的更简单的方法是启用 jquery.validate.unobtrusive。对于服务器上的东西来说,一切都很正常。

但是当我尝试用 javascript 注入一些新的“输入”时,我知道我需要调用 $.validator.unobtrusive.parse()来重新绑定验证。但是,所有这些动态注入的磁场仍然不能正常工作。

更糟糕的是,我尝试使用 jquery.validate手动绑定,它也不工作。有什么想法吗?

79816 次浏览

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");

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

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/

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");

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();

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');

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")

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);

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');

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!

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');
}
});

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");

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.