Get a list of checked models in controller

92 views Asked by At

Controller:

[HttpPost]
public ActionResult GetChecked(FormCollection formCollection)
{
    var checked = formCollection["Checked"].Split(',');
    var ids = formCollection["Original.ID"].Split(',');
}

View:

@model IEnumerable<Models.Entry> []

<table>
@using (Html.BeginForm("GetChecked", "ControllerName"))
{
        @foreach (var item in Model[0])
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Original.ID)
                    @Html.HiddenFor(modelItem => item.Original.ID)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Original.Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.New.Name)
                </td>
                <td>
                    @Html.CheckBoxFor(modelItem => item.Checked)
                </td>
            </tr>
        }
</table>

//Then there's another table with Model[1]

Model

public class Entry
{
    public Entry()
    {
        Original = new SomeObject();
        New = new SomeObject();
        Checked = false;
    }

    public SomeObject Original { get; set; }
    public SomeObject New { get; set; }
    public bool Checked { get; set; }
}

This works but the ids-array in my controller gets both a true- and a false value for the checked rows. I read that it's because of the FormCollection. Question: How can I let GetChecked take an IEnumerable<Models.Entry> as a parameter instead? When I tried it it resulted in a null value.

2

There are 2 answers

1
Peter B On BEST ANSWER

There are a couple of things that you should change:

  • When rendering controls from a list or array using CheckBoxFor, EditorFor, etc. you should never use foreach - instead, ALWAYS use a for-loop and apply indexing to your collection. The reason is that indexing creates numbered items in your <form> that then no longer conflict with each other, and those numbered items are precisely what you need to successfully process a list of submitted values. See this answer for a simple example: https://stackoverflow.com/a/15375949/1220550

  • Don't use FormCollection, use a ViewModel class instead. By using FormCollection you are giving up on Databinding / ModelState / ValidationSummary which together are a superb feature of ASP.NET MVC. It is too much to explain it all here, but here is a great link that does exactly that.

  • It is best to use a fully defined ViewModel class, not only for databinding (see before) but also for consistency and ease-of-use. Having an array of IEnumerable<X> as your @model is at best confusing, and at worst a cause for errors. And what if suddenly you also want to pass an int? Impossible with IEnumerable<X>[], yet a piece of cake with a ViewModel - just add it to the class.

0
Zinov On

The result is null because you should bind your IEnumerable interface with a model binder. I think you are looking to create a model binding provider, because a provider can look at the type of the property and then create your custom model binder just for that property.

Take a look on this link also http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx