Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC - Model binding a set of dynamically generated checkboxes - how to

I'm trying to model bind a set of dynamically generated checkboxes so as to process them in the controller action but can't get the model binding to occur. This is the scenario:

My ViewModel class (DocumentAddEditModel) contains a dictionary (Dictionary<string,bool>) with the string of each entry being the name/label for each checkbox and the boolean indicating whether the checkbox is checked:

    public class DocumentAddEditModel
    {
        ...
        private Dictionary<string, bool> _categoryCheckboxes = new Dictionary<string,bool>();
        ...

        ...
        public Dictionary<string, bool> CategoryCheckboxes
        {
            get { return _categoryCheckboxes; }
            set { _categoryCheckboxes = value; }
        }
        ...
    }
}

Within my controller the action that handles the GET request for the form populates the dictonary as follows:

public class DocumentsController : Controller
{
    [RequiresAuthentication]
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Add()
    {
        DocumentAddEditModel documentAddEditModel = new DocumentAddEditModel();
        ...
        Dictionary<string, bool> categoryCheckboxes = new Dictionary<string, bool>();
        ...
        string[] categories = Enum.GetNames(typeof(Category));

        foreach (string category in categories)
            categoryCheckboxes.Add(category, false);

        documentAddEditModel.CategoryCheckboxes = categoryCheckboxes;

        return View(documentAddEditModel);
    }
}

Within the view i have the following to generate the checkboxes:

<% foreach (KeyValuePair<string, bool> categoryCheckbox in ViewData.Model.CategoryCheckboxes)
    {%>
    <input class="checkbox" type="checkbox" name="CategoryCheckboxes[0].Key" id="<%= categoryCheckbox.Key %>" />
    <label class="categoryLabel" for="<%= categoryCheckbox.Key %>"><%= categoryCheckbox.Key %></label>
<% } %>

but i think this is where the problem must be. Not sure what needs to be going in the name attribute. The problem is that once posted back to the following action method in the DocumentsController:

[RequiresAuthentication]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Add(DocumentAddEditModel documentAddEditModel)
{
    ...
}

documentAddEdit.Model.CategoryCheckboxes is always null. How do i set this up so that the CategoryCheckboxes dictionary is correctly populated with name and checked/unchecked bool value for the checkboxes?

Thanks

like image 656
Matthew Avatar asked Jun 23 '09 08:06

Matthew


2 Answers

If you are binding your checkboxes to a Dictionary<string, bool> try this:

<% var i = 0; %>
<% foreach (KeyValuePair<string, bool> categoryCheckbox in Model.CategoryCheckboxes) {%>

    <input type="hidden" name="<%= String.Format("CategoryCheckboxes[{0}].Key", i) %>" value="<%= categoryCheckbox.Key %>" />
    <%= Html.CheckBox(String.Format("CategoryCheckboxes[{0}].Value", i), categoryCheckbox.Value) %>

    <label class="categoryLabel" for="<%= categoryCheckbox.Key %>"><%= categoryCheckbox.Key %></label>

    <% i++; %>
<% } %>

Hope this helps

UPDATED:

For binding to IDictionary<T1, T2> your form must contain inputs with "CategoryCheckboxes[n].Key" and "CategoryCheckboxes[n].Value" Ids/Names, where n must be zero-based and unbroken.

like image 171
eu-ge-ne Avatar answered Oct 27 '22 21:10

eu-ge-ne


hidden elements are the modus operandi of asp.net. in the webforms version it was the ViewState, which was a massive hidden field, in the MVC its lighter weight more straightforward hidden fields per control. I'm not sure what's wrong or disturbing about that. Somehow you have to keep state, you can either do it client side or server side. I suggest client side for non-sensitive information that way it will still be there if the server is restarted between postbacks etc.

like image 28
dave Avatar answered Oct 27 '22 19:10

dave