1
votes

I have a view that is populated from a view model with one of the properties being a collection of complex types. The view renders correctly with the list being iterated into a table with a radio button for each item which has not already been selected. When I submit the form I cannot get the list of objects back into the model and I can't figure out what I'm doing wrong. Each item in the collection is added to the view as below:

@for (int i = 0; i < Model.Items.Count; i++
    var rowClass = "selectRow";

    if (item.IsSelected)
    {
        rowClass = "success";
    }


    <tr class="@rowClass">
        @Html.HiddenFor(m => m.Items[i].PropertyOne)
        <td>
            @Html.DisplayFor(m =>  m.Items[i].PropertyTwo)
        </td>
        <td class="actions">
            // only those items not previously selected need a radio button
            @if (item.IsSelected == false)
            {
                @Html.RadioButtonFor(m =>  m.Items[i].IsSelected,  m.Items[i].PropertyOne, new { id = "IsSelected_" +  m.Items[i].PropertyOne })
            }
        </td>
    </tr>
}

I have tried using @Html.HiddenFor(x => x.PropertyOne) but I cannot bind the collection of selected values back to the model. I can return everything else in the model using hidden fields but I have no clue how to fix this. Any help is appreciated.

Thanks

1
The best way to figure out what's going on here is to see what Model.Items is on the wire when you post. Have you used Fiddler? telerik.com/fiddler. Or FireBug? - AJ.

1 Answers

2
votes

If you want collections to post correctly to the model binder, you cannot use a foreach loop (unless you are using EditorFor, see comment). You have to use a good old fashioned for loop and have your collection derived from IList. See my question and the answers here for more info.

Additionally, any property of the collection object type that you want posted, you need to assure that it is used in the loop. So, say your Item class looks like this:

public class Item
{
    public int ItemId { get; set; }
    public string ItemDescription { get; set; }
    public bool IsSelected { get; set; }
}

...your loop might look like this:

@for(var i = 0;i < Model.Items.Count; i++)
{
    <tr>
        @Html.HiddenFor(m => Model.Items[i].ItemId)
        <td>
             @Html.TextBoxFor(m => Model.Items[i].Description)
        </td>
        <td>
             @Html.CheckBoxFor(m => Model.Items[i].IsSelected)
        </td>
    </tr>
}

It's also important to note that simply displaying a model property will not post it. Meaning that...

@Model.Items[i].Description

...will not post, but hiding it or using it in an HTML element...

@Html.HiddenFor(m => Model.Items[i].Description)

will post and bind correctly.

One of the definitive articles on this subject from Phil Haack can be found here.