Dandry Dandry - 6 months ago 45
Ajax Question

MVC - jQuery validation does not work on dynamically added elements

I know this kind of issue has been solved many times already, however I am unable to get mine fixed based on solutions provided.

I am building a simple library application. There is a feature to add a copy of a book, which uses jQuery to invoke controller actions and return partial views which are then added dynamically to the DOM.

The last dynamically element added is a form with additional details of a created copy. The ajax call is being triggered when a value of a

DropDownList
(
#AuthorBooksDropDown
) (also added dynamically) changes.

$('#authorBooksPlaceHolder').on('change', '#AuthorBooksDropDown', function () {

var bookId = $(this).val();

$.get('/Books/AddCopy_RenderDetails/' + bookId, function (data) {

$('#bookDetailsPlaceHolder').html(data);
$('#bookDetailsPlaceHolder').slideDown();
});

$.validator.unobtrusive.parse('#addCopyForm');
});


The call invoked the
AddCopy_RenderDetails
action get an entity from a DB based on book
id
, and creates a new copy with certain fields populated.

Controller action:

public PartialViewResult AddCopy_RenderDetails(int id)
{
var book = db.LibraryBooks.Find(id);

var newCopy = new Book()
{
Author = book.Author,
Title = book.Title,
Publisher = book.Publisher,
CollectionId = book.CollectionId,
Collection = book.Collection
};

return PartialView("_AddCopy_Details", newCopy);
}


The view displays remaining fields which need to be populated.

@model CityLibrary.Models.Library.Book

<div class="vertical-separator"></div>

<hr />

@using (Ajax.BeginForm("AddCopy", "Books", new AjaxOptions
{
UpdateTargetId = "bookDetailsPlaceHolder"
}, new { @id = "addCopyForm" }))
{
@Html.AntiForgeryToken()

@Html.HiddenFor(model => model.Author)
@Html.HiddenFor(model => model.Title)
@Html.HiddenFor(model => model.CollectionId)
@Html.HiddenFor(model => model.Collection.Name)
@Html.HiddenFor(model => model.Publisher)

<div class="form-group">
@Html.LabelFor(model => model.Collection.Name, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Collection.Name, new { htmlAttributes = new { @class = "form-control", @disabled = "" } })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.ISBN, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ISBN, new { htmlAttributes = new { @class = "form-control", } })
@Html.ValidationMessageFor(model => model.ISBN, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.Publisher, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Publisher, new { htmlAttributes = new { @class = "form-control", @disabled = "disabled" } })
@Html.ValidationMessageFor(model => model.Publisher, "", new { @class = "text-danger" })
</div>
</div>

<div class="form-group">
@Html.LabelFor(model => model.YearPrinted, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.YearPrinted, new { htmlAttributes = new { @class = "form-control", @Value = "" } })
@Html.ValidationMessageFor(model => model.YearPrinted, "", new { @class = "text-danger" })
</div>
</div>


<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-success btn-block" />
</div>
</div>
}


Even though I have
$.validator.unobtrusive.parse('#addCopyForm');
invoked when the form is rendered (checked in chrome dev tools), validation still happens on the server side upon pressing a submit button as a POST action is being triggered every time. Not to mention that validation errors do not display upon TABing to next field.

Validation attributes are there in form's inputs:

enter image description here

I also have remote validation which checks whether a entered ISBN is already in the database. Obviously this works on the client side, which in my case simply does not.

Thank you for your time and help.

EDIT:

Well, I've added the following to the end of the view:

<script>
$.validator.unobtrusive.parse('#addCopyForm');
</script>


And it works. I have no idea why triggering it on a function does nothing.

Answer

Ajax is async, and your $.validator.unobtrusive.parse('#addCopyForm'); line of code is being called before the html has been added to the DOM. Move it to inside the success callback

$.get('/Books/AddCopy_RenderDetails/' + bookId, function (data) {
    $('#bookDetailsPlaceHolder').html(data);
    $('#bookDetailsPlaceHolder').slideDown();
    $.validator.unobtrusive.parse('#addCopyForm');
});