ASP.Net MVC returning values from List of Checkboxes
I have a model with a property that is a List. MyObjects simply has an id, a description and a selected boolean property.
I have managed to display the items as checkboxes on my view. I did this via:
<%foreach (var cat in Model.DefaultCategories)
{%>
<tr>
<td>
<%=cat.Category %>
</td>
<td>
<%=Html.CheckBoxFor(x=>cat.Selected) %>
</td>
</tr>
<%
}%>
</table>
However, there is a problem. They all end up, when rendered, with the same names. Here's a portion of my list:
<tr>
<td>
Medical
</td>
<td>
<input id="cat_Selected" name="cat.Selected" type="checkbox" value="true" /><input name="cat.Selected" type="hidden" value="false" />
</td>
</tr>
<tr>
<td>
Salary
</td>
<td>
<input checked="checked" id="cat_Selected" name="cat.Selected" type="checkbox" value="true" /><input name="cat.Selected" type="hidden" value="f开发者_运维问答alse" />
</td>
</tr>
They have all been named "cat.Selected".
How can I resolve this?
And then, when I submit, I need to iterate through them. With different names, I assume I can get them in my HttpPost method:
[HttpPost]
public ActionResult Modify(int id, FormCollection formValues)
{
PayeeDto p = new PayeeDto { Name = Request.Form["name"], PayeeId = id };
Services.PayeeServices.Save(p);
return RedirectToAction("Index");
}
The FormCollection will have the different names? At the moment, it just has the single 'cat.selected' item.
There is a way you can submit collections to your action by using names with []
. As described here http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
<% for (int i = 0; i < Model.DefaultCategories.Count; i++) { %>
<td>
<input type="checkbox" name="[<%= i %>].Selected" <% Model.DefaultCategories[i].Selected ? "checked=\"checked\"" : string.Empty %>/>
</td>
<% }%>
Then your action can take a collection of models like so
public ActionResult Modify(int id, ICollection<UpdateModel> updates)
{}
I would recommend you using Editor Templates and stop writing loops in your views. They will take care of generating the proper names so that binding works. Example:
In your main view:
<table>
<thead>
<tr>
<th>Name</th>
<th>Selected</th>
</tr>
</thead>
<tbody>
<%: Html.EditorFor(x => x.DefaultCategories) %>
</tbody>
</table>
and then inside an editor template strongly typed to a Category (~/Views/Home/EditorTemplates/Category.ascx
). Also if you want to get the corresponding Name back in your controller action you need to include it (probably as hidden field). Another technique involves adding only the id and then fetching back the relevant information from the database in your controller action:
<%@ Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.Category>" %>
<tr>
<td><%: Model.Name %></td>
<td>
<!-- include the category name as hidden field so that
we can fetch it back in the controller action
-->
<%: Html.HiddenFor(x => x.Name) %>
<%: Html.CheckBoxListFor(x => x.Selected) %>
</td>
</tr>
Now the naming convention is important here. If the DefaultCategories property on your view model is an IEnumerable<Category>
, then the editor template needs to be called Category.ascx and placed in ~/Views/Home/EditorTemplates/Category.ascx
or if it will be reused between multiple controllers in ~/Views/Shared/EditorTemplates/Category.ascx
.
Also your controller action you are submitting to should use a view model as parameter:
[HttpPost]
public ActionResult Modify(MyViewModel model)
{
PayeeDto = Mapper.Map<MyViewModel, PayeeDto>(model);
Services.PayeeServices.Save(p);
return RedirectToAction("Index");
}
This may not be the best answer but I try not to use generated checkboxes in MVC.
I would change
<td>
<%=Html.CheckBoxFor(x=>cat.Selected) %>
</td>
To
<td>
<input type="checkbox" name="<%: cat.value %>" id="<%: cat.value %>" <% cat.Selected ? " checked=\"checked\" " : ""; %> />
</td>
精彩评论