1

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

tereško
  • 58,060
  • 25
  • 98
  • 150
crunchy
  • 705
  • 1
  • 13
  • 35
  • 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? http://www.telerik.com/fiddler. Or FireBug? – AJ. Feb 13 '14 at 19:54

1 Answers1

2

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.

Community
  • 1
  • 1
AJ.
  • 16,368
  • 20
  • 95
  • 150
  • I have looked at this but have been unable to get the view to submit when I change the foreach to a for loop. – crunchy Feb 13 '14 at 19:22
  • See my edit, and post your revised code with the `for` loop in your original question as an edit so we can see what you've got. – AJ. Feb 13 '14 at 19:25
  • 1
    hi @AJ, your answer is correct. One thing is it doesn't have to use `for`, you still can use foreach. take a look this link:http://stackoverflow.com/questions/20842935/asp-net-mvc-complex-model-updating/20843172#20843172 – Lin Feb 13 '14 at 19:34
  • @Lin - Thanks, I didn't know that `EditorFor` would handle a `foreach` across the wire. – AJ. Feb 13 '14 at 19:37
  • @crunchy - I don't believe `DisplayFor` will bind, and other than the missing right paranthesis in the `for` loop, your code looks OK. Have you Fiddled it to see what's getting posted? – AJ. Feb 13 '14 at 19:41