Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC EditorFor model binding for multiple edit forms on one page

I have a class called CategoryModel, one of the properties of which is a list of objects of the same type. So CategoryModel.Categories is of type List<CategoryModel>.

On the category index page, I display an editor for each category so that the user can change any of the category names without having to go to a dedicated page to do so. Like so:

<ul id="categories>
    @Html.EditorFor(model => model.Categories)
</ul>

And the editor template for CategoryModel looks like this:

<li class="folder">
    @using (Html.BeginForm("Edit", "Category", new { id = Model.Key }, FormMethod.Post, new { @class = "ajaxoff"})) {
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.Key)
        @Html.HiddenFor(model => model.ParentKey)
        @Html.HiddenFor(model => model.Sequence)
        @Html.HiddenFor(model => model.IncludeDeleted)

        @Html.TextBoxFor(model => model.Name, null, new { @class = "catName" })
        @Html.ValidationMessageFor(model => model.Name)

        <input type="submit" value="Save" class="icon save" />
    }
</li>

The problem I have is that submitting the form does not bind correctly to the Edit action of the CategoryController:

[HttpPost]
public ActionResult Edit(CategoryModel category)
{
    // At this point all properties in category are null
}

If I check the names on the hidden fields and textboxes, they are labelled based on their position in the current category (e.g. Categories[0].Name). If I create a dedicated edit view, however, they are simply named according to the field name (e.g. Name).

I have tried changing the controller to accept a list of Categories:

[HttpPost]
public ActionResult Edit(List<CategoryModel> categories)
{
    var category = categories.First();
}

This works if I submit the very first category, but none of the others (in those cases Categories is null).

I have also tried changing how I display my EditorFor, by doing this:

<ul id="categories>
    @foreach (var cat in Model.Categories)
    {
        @Html.EditorFor(model => cat);
    }
</ul>

Which changes the field names to be the same for each category (e.g. all category names are called cat.Name), which I believe is a step in the right direction.

So how do I bind correctly to my controller? I realise that I could submit the whole parent category and then save each subcategory, but this seems like a very inefficient way to submit a single change.

like image 375
Maloric Avatar asked Jul 12 '13 13:07

Maloric


1 Answers

I discovered how to do this. There is an overload for Html.EditorFor that allows you to specify the htmlFieldName property (the third parameter in the example below):

@foreach (var cat in Model.Categories)
{
    @Html.EditorFor(model => cat, null, "");
}

This renders all of the field names without any prefix and allows me to submit any single category successfully.

like image 70
Maloric Avatar answered Oct 23 '22 23:10

Maloric