Bind dynamically created element to complex mvc model in view

asp.net-mvc, asp.net-mvc-4, c#, jquery

Solution

It seems a reasonable enough approach, but You've not shown your EditorTemplate, so I'm going to assume its something like:

@model List<something>

@for(int i = 0; i < Model.Count; i++)
{
   <tr>
     <td>@Html.DisplayFor(m => m[i].Id) @Html.HiddenFor(m => m[i].Id)</td>
     <td>@Html.EditorFor(m => m[i].Name)</td>
   </tr>
}

Your ajax method should return the HTML of a row - and this is important... the form fields need to be named 1 above the last one in the table.

So when you view the rendered source of your table (before adding any new fields it might look like:

...
<tbody>
   <tr>
     <td>1 <input type="hidden" name="something[0].Id" value="1"/></td>
     <td><input type="text" name="something[0].Name" value="somename" /></td>
   </tr>
</tbody>

You need to ensure the html returned by the ajax method for your new row is:

   <tr>
     <td>2 <input type="hidden" name="something[1].Id" value="2"/></td>
     <td><input type="text" name="something[1].Name" value="somenewname" /></td>
   </tr>

ie. the number inside the brackets is the next index for the items in something. If there is a gap in the indexes (or they overlap) then the new items will not get parsed.

EDIT - to get client side validation to work for the new fields alter your jquery ajax success callback as follows:

$('#AddSomething').click(function () {
    $.ajax({
        url: '@Url.Action("AddSomething", "SomethingModels")',
        data: { tableSize: $('#SomethingTable tr').length },
        cache: false,
        success: function (html) { 
            $('#SomethingTable tr:last').after(html);
            $.validator.unobtrusive.parse('#SomethingTable');
        }
    });

Problem

I have a very huge form in my application with a lot of different inputs and a lot of lists in my model. So i will try to add/delete the lists without sending the complete model to the server. I tried several ways now but i don´t find a clean way. You can imagine my model like: ``` public class EditSomething { public string name { get; set;} public List<something> somethingList { get; set;} // a lot other fields... public EditSomething(EditSomethingFromDatabase editSomethingFromDatabase) { name = editSomethingFromDatabase.Name; somethingList = new List<SomethingModel>(); foreach(var something in editSomethingFromDatabase.Something) { somethingList.Add(new SomethingModel(editSomethingFromDatabase.Something)); } } } ``` The other model looks similar but without lists. In the view i have a table for the model: ``` <h2>Something</h2> <div id="SomethingDiv"> <table id="SomethingTable"> <thead> <tr> <th>@Html.Label("SomethingName")</th> <th>@Html.Label("SomethingID")</th> <th></th> </tr> </thead> <tbody id="SomethingTableBody"> @Html.EditorFor(x => x.somethingList) </tbody> </table> <p> <input type="button" name="addSomething" value="Add Something" id="AddSomething"> </p> </div> ``` the jquery of the addSomething is: ``` $('#AddSomething').click(function () { $.ajax({ url: '@Url.Action("AddSomething", "SomethingModels")', data: { tableSize: $('#SomethingTable tr').length }, cache: false, success: function (html) { $('#SomethingTable tr:last').after(html); } }); ``` The controller method AddSomething is: ``` public ActionResult AddSomething (int tableSize) { SomethingModel something= new SomethingModel(null, (-2) * (tableSize + 1)); return PartialView(""~/Views/EditorTemplates/EditSomethingModel.cshtml"", something); } ``` And at least i have a editor template in EditorTemplates as for editorfor and partialview. This have the important informations i want to send to the server: ``` @model SomethingModel <tr>@TextBoxFor(m=>m.SomethingName)<td> ``` @TextBoxFor(m=>m.SomethingID) So the problem now is, that the submit of the first view only post the `SomethingModel` to the server who already existed while opening the view but the new SomethingModel from the `AddMutation` method aren´t in the post. Someone an idea to fix this? Edit: Changed the path to the editor template so i only need one view for the `EditorFor` and `PartialView`. Edit2: To solve the main problem i created a view as following and use it as partial view. Now the data is send to the server correctlly. Only the validation on client side is still not working: ``` @model SomethingModel <tr>@TextBoxFor(m=>m.SomethingName, new{Name="somethingList["+ViewBag.ListId+"].SomethingName")<span class="field-validation-valid" data-valmsg-for="somethingList[@ViewBag.ListId].SomethingName" data-valmsg-replace="true"></span><td> <tr>@TextBoxFor(m=>m.SomethingID, new{Name="somethingList["+ViewBag.ListId+"].SomethingID")<span class="field-validation-valid" data-valmsg-for="somethingList[@ViewBag.ListId].SomethingID" data-valmsg-replace="true"></span><td> </tr> ``` In the `AddSomething` method i added the `ViewBag.ListId` with the id of the next element in the list.

Original source