Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bind dynamically created element to complex mvc model in view

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.

like image 685
PX Roth Avatar asked Sep 08 '26 10:09

PX Roth


1 Answers

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');
        }
    });
like image 196
James S Avatar answered Sep 09 '26 23:09

James S



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!