I have written a partial view that displays a list of rows, where some fields in the row are editable using a textbox, checkbox, or whatever.
I would like the parent view to have a “submit” button that posts the whole collection of updated rows so that I can do a group update rather than posting once for every updated row.
Here’s what I have:
public class GroupModel {
public string SomeGenericProperty { get; set; }
public IEnumerable<ItemModel> Items { get; set; }
}
public class ItemModel {
public long ID { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
Then I have a view “GroupDetails”:
@model MyNamespace.GroupModel
...
@using (Html.BeginForm("SaveItems", "Home")) {
<fieldset>
...
@Html.Partial("ItemList", Model.Items)
...
<input type="submit" value="Approve" />
</fieldset>
}
And a partial view “ItemList”:
@model IEnumerable<MyNamespace.ItemModel>
<table>
<tr>
<th>
Field 1
</th>
<th>
Field 2
</th>
<th>
</th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.TextBoxFor(modelItem => item.Field1)
</td>
<td>
@Html.TextBoxFor(modelItem => item.Field2)
</td>
<td>
@Html.HiddenFor(i => item.ID)
</td>
</tr>
}
</table>
But when I post, the information from partial view is not being posted; the GroupModel object’s Items property is null.
What’s the right way to do this?
I would recommend you to always use editor templates. The reason your code doesn’t work is because you used a partial and this partial doesn’t inherit the parent template context which means that helpers that are used inside it will generate wrong names for the input fields.
For example if you look at the source code of your page you will see this:
instead of the correct name which is:
I would recommend you reading the following blog post to better understand the wire format that the default model binder expects.
So start by fixing your main view first and replace the partial with an editor template:
and then define a custom editor template that will be rendered for each element of the model (
~/Views/Shared/EditorTemplates/ItemModel.cshtml– this works by convention, it should be either inside~/Views/Shared/EditorTemplatesif you want it to be reused among controllers or inside~/Views/Home/EditorTemplatesif you want this template to be available only for the Home controller – and the name of the template must be the type of the collection – in your case it isItemModel.cshtml):Now everything will bind fine and dandy in your controller action: