I am trying to bind to a Model that has a collection property, specifically a List. For the purposes of this example, this represents a list of user roles:
public class RolesModel
{
private List<SelectListItem> _Roles = null;
public string Name { get; set; }
public List<SelectListItem> Roles
{
get {
if (_Roles == null) { _Roles = new List<SelectListItem>(); }
return _Roles;
}
set { _Roles = value; }
}
}
I am binding this to a strongly-typed view via the following Controller:
public class TestController : Controller
{
RolesModel myModel = new RolesModel();
[HttpGet]
public ActionResult Edit()
{
myModel.Name = "Joe Bloggs";
myModel.Roles = new List<SelectListItem>
{
new SelectListItem { Value = "1", Text = "Member", Selected = true },
new SelectListItem { Value = "2", Text = "Manager", Selected = true },
new SelectListItem { Value = "3", Text = "Administrator", Selected = false }
};
return View(myModel);
}
[HttpPost]
public ActionResult Edit(RolesModel m)
{
// !!! m.Roles is always empty !!!
return View("Results", m);
}
}
This then invokes the following view:
@model MyProject.WebUI.Models.RolesModel
@using (Html.BeginForm())
{
<p>
@Html.LabelFor(m => m.Name)
@Html.EditorFor(m => m.Name)
</p>
<div>
@Html.EditorFor(m => m.Roles, "CheckBoxList")
</div>
<p>
<input type="submit" value="Save" />
</p>
}
Note the template specific call to my custom editor template in ‘/Views/Shared/EditorTemplates/CheckBoxList.cshtml’ this looks like this:
@model List<System.Web.Mvc.SelectListItem>
<h3>Type: @Html.LabelFor(m => m)</h3>
<ul>
@for (int i = 0; i < Model.Count; i++)
{
<li>
@Html.CheckBoxFor(m => m[i].Selected)
@Html.LabelFor(m => m[i].Selected, Model[i].Text)
@Html.HiddenFor(m => m[i].Value)
</li>
}
</ul>
The idea being that each SelectListItem is represented by the Html rendered by the loop.
The first part of the process appears to work correctly, The form is presented as expected and you can update the ‘Name’ text box and the check/uncheck the checkboxes.
The problem is that when the form is posted back to the controller, the Roles collection is never populated.
I’m new to MVC and thought that the framework actually re-constructed the model data from the post via the enforced form element naming convention. I’m obviously missing an important point and I’m hoping someone can point me in the right direction.
Thanks, and apologies for the long post.
Here’s how you could proceed:
and inside the EditorTemplate (
/Views/Shared/EditorTemplates/SelectListItem.cshtml):Notice the simplification of the editor template. It no longer takes a
List<SelectListItem>as model but simply aSelectListItem. It will automatically be invoked for each element of the Roles collection so that you don’t need to write any loops. Just follow the conventions.I would also simplify your view model like this:
and your controller: