MVC 4 binding nested list of lists result

2019-05-25 07:40发布

问题:

I've done some research on this and seem to find posts that are either outdated or don't quite work in my situation. I may be using wrong keywords when searching though... =/

On my web page I have Tabs containing Group boxes that contain Lines which in turn contain Items. So it is lists of lists 4 levels.

The problem:

When posting back, ViewModel.Tabs is null and I can't save anything. Everything displays quite nicely, but nothing is posted back.

The code

Views/Shared/EditorTemplates/Tab.cshtml

@model AWMCCRM.Web.ViewModels.Tab

@Html.HiddenFor(vm => vm.Name)
<div id="tab-@Model.Name.Replace(" ", string.Empty)" class="tab-content two">
     @Html.EditorFor(vm => vm.Groups)
</div>

Views/Shared/EditorTemplates/Group.cshtml

@model AWMCCRM.Web.ViewModels.Group
@Html.HiddenFor(vm => vm.Name)
<fieldset>
    <legend>@Model.Name</legend>
    @Html.EditorFor(vm => vm.Lines)
</fieldset>

Views/Shared/EditorTemplates/Line.cshtml

@model AWMCCRM.Web.ViewModels.Line
<div class="_100Max">
@Html.HiddenFor(vm => vm.Name)
@Html.EditorFor(vm => vm.Items)
</div>

Views/Shared/EditorTemplates/Item.cshtml

@model AWMCCRM.Web.ViewModels.Item

<div class="_@Model.DisplaySize" title="@Model.Description">
    <p>
        @Html.HiddenFor(x => x.DataType)
        @Html.HiddenFor(x => x.Description)
        @Html.HiddenFor(x => x.DisplaySize)
        @Html.HiddenFor(x => x.DisplayType)
        @Html.HiddenFor(x => x.IDsPiped)
        @Html.HiddenFor(x => x.ItemType)
        @Html.HiddenFor(x => x.Name)

        @Html.LabelFor(vm => vm.Value, Model.Name)
        @switch (Model.DisplayType.ToLower().Replace(" ", string.Empty))
        {
            case "checkbox":
                @Html.CheckBoxFor(vm => Convert.ToBoolean(vm.Value))
                break;
            case "dropdownlist":
                @Html.DropDownListFor(vm => vm.Value, Model.ValueOptionListItems)
                break;
            case "multiselectlist":
                @Html.ListBoxFor(
                    x => x.SelectedValueList,
                    Model.ValueOptionListItems, 
                new { id = "itemValuesMultiSelect", multiple = "multiple", Size = 15 })
                break;
            case "radiobutton":
                @Html.RadioButtonFor(vm => vm.Value, Model.Value)
                break;
            case "textarea":
                @Html.TextAreaFor(vm => vm.Value)
                break;
            default:
                @Html.TextBoxFor(vm => vm.Value)
                break;
        }
    </p>
</div>

The ViewModel (cut down version)

namespace AWMCCRM.Web.ViewModels
{
    public class PersonEditViewModel
    {
        public List<Tab> Tabs { get; set; }

        //Other properties
        //...

    }
}

The View (cut down version)

@using (Html.BeginForm("Edit", "Person", FormMethod.Post, new { id = "validate-form", @class = "block-content form" }))
{
    @Html.AntiForgeryToken()
    @Html.HiddenFor(x => x.PersonID)
    @foreach (var tab in Model.Tabs)
    {  
        @Html.EditorFor(vm => tab)
    }
    <input class="close-toolbox button" type="submit" value="Save">
}

Any suggestions?

Thanks

回答1:

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

It's an old article, but it still applies.

I had this exact problem, but basically due to the model binding system you need to use an explicit for loop instead of a foreach loop, and reference your elements by their index.

@using (Html.BeginForm("Edit", "Person", FormMethod.Post, new { id = "validate-form", @class = "block-content form" }))
{
    Html.AntiForgeryToken()
    Html.HiddenFor(x => x.PersonID)
    for (int i = 0; i<Model.Tabs.Count; i++)
    {
        Html.EditorFor(x => Model.Tabs[i])
    }
    <input class="close-toolbox button" type="submit" value="Save">
}