2

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="false" />
        </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.

3 Answers 3

3

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)
{}
Sign up to request clarification or add additional context in comments.

Comments

2

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");
}

2 Comments

Thanks - can you maybe explain the Mapper.Map? That sounds interesting.
@cdotlister, you may take a look at AutoMapper. But this could be any mapping technique you could use (even one that you wrote manually). The idea is that your controller action should receive a view model as parameter that needs to be mapped to a model and passed to the repository. How this mapping happens is off topic here.
1

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.