How to use Html.CheckBox (list) with IEnumerable <T> with validation
I work on a page where the user must fill in some information and finally make a choice of 1 or more clients using checkboxes.
The client list is the IEnumerable<Customer> , which I pass to my Model. How can I create a .CheckBoxFor() using .CheckBoxFor() ?
And finally, I would like to be able to check if at least 1 checkbox is selected.
A request is an object that stores information entered by the user.
<% foreach (var customer in Model.Request.Customers) { %> <%= Html.CheckBoxFor(/* customer */) %> <% } %> Can someone point me in the right direction? Or am I all about this wrong?
You can create your own html extension class and overload the CheckBoxFor method, as shown below. The method evaluates metadata. Model for the value passed to it (for example, in the US state). You can get the value / s of the checkbox from FormCollection in ControllerAction:
public ActionResult Edit(FormCollection formCollection) { // Get the value(s) string checkBox = formCollection["State"]; // perform validation .... } Example assumes a general list of a key pair
<% foreach (var element in UnitedStatesDictionary()) { %> <%= Html.CheckBoxFor(model => model.State, null, element.Key) %><%= element.Value %><br /> <% } %> HtmlExtensions.cs
using System; using System.Linq; using System.Linq.Expressions; using System.Web.Mvc; using System.Web.Routing; public static class HtmlExtensions { /// <summary> /// Checks the box for. /// </summary> /// <typeparam name="TModel">The type of the model.</typeparam> /// <typeparam name="TValue">The type of the value.</typeparam> /// <param name="html">The HTML.</param> /// <param name="expression">The expression.</param> /// <returns>Checkbox</returns> public static MvcHtmlString CheckBoxFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) { return CheckBoxFor(html, expression, new RouteDirection()); } /// <summary> /// Checks the box for. /// </summary> /// <typeparam name="TModel">The type of the model.</typeparam> /// <typeparam name="TValue">The type of the value.</typeparam> /// <param name="html">The HTML.</param> /// <param name="expression">The expression.</param> /// <param name="htmlAttributes">The HTML attributes.</param> /// <returns>Checkbox</returns> public static MvcHtmlString CheckBoxFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes) { return CheckBoxFor(html, expression, htmlAttributes, ""); } /// <summary> /// Checks the box for. /// </summary> /// <typeparam name="TModel">The type of the model.</typeparam> /// <typeparam name="TValue">The type of the value.</typeparam> /// <param name="html">The HTML.</param> /// <param name="expression">The expression.</param> /// <param name="htmlAttributes">The HTML attributes.</param> /// <param name="checkedValue">The checked value.</param> /// <returns>Checkbox</returns> public static MvcHtmlString CheckBoxFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes, string checkedValue) { ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); string htmlFieldName = ExpressionHelper.GetExpressionText(expression); TagBuilder tag = new TagBuilder("input"); tag.Attributes.Add("type", "checkbox"); tag.Attributes.Add("name", metadata.PropertyName); if (!string.IsNullOrEmpty(checkedValue)) { tag.Attributes.Add("value", checkedValue); } else { tag.Attributes.Add("value", metadata.Model.ToString()); } if (htmlAttributes != null) { tag.MergeAttributes(new RouteValueDictionary(htmlAttributes)); } if (metadata.Model.ToString() == checkedValue) { tag.Attributes.Add("checked", "checked"); } return MvcHtmlString.Create(tag.ToString(TagRenderMode.SelfClosing)); } } While I am here, here is my list of the United States for code completion:
/// <summary> /// United States dictionary. /// </summary> /// <returns>List of United States</returns> public static List<KeyValuePair<string, string>> UnitedStatesDictionary() { var arrList = new List<KeyValuePair<string, string>>(); arrList.Add(new KeyValuePair<string, string>("AL", "Alabama")); arrList.Add(new KeyValuePair<string, string>("AK", "Alaska")); arrList.Add(new KeyValuePair<string, string>("AZ", "Arizona" )); arrList.Add(new KeyValuePair<string, string>("AR", "Arkansas" )); arrList.Add(new KeyValuePair<string, string>("CA", "California" )); arrList.Add(new KeyValuePair<string, string>("CO", "Colorado" )); arrList.Add(new KeyValuePair<string, string>("CT", "Connecticut" )); arrList.Add(new KeyValuePair<string, string>("DE", "Delaware" )); arrList.Add(new KeyValuePair<string, string>("DC", "District Of Columbia" )); arrList.Add(new KeyValuePair<string, string>("FL", "Florida" )); arrList.Add(new KeyValuePair<string, string>("GA", "Georgia" )); arrList.Add(new KeyValuePair<string, string>("HI", "Hawaii" )); arrList.Add(new KeyValuePair<string, string>("ID", "Idaho" )); arrList.Add(new KeyValuePair<string, string>("IL", "Illinois" )); arrList.Add(new KeyValuePair<string, string>("IN", "Indiana" )); arrList.Add(new KeyValuePair<string, string>("IA", "Iowa" )); arrList.Add(new KeyValuePair<string, string>("KS", "Kansas" )); arrList.Add(new KeyValuePair<string, string>("KY", "Kentucky" )); arrList.Add(new KeyValuePair<string, string>("LA", "Louisiana" )); arrList.Add(new KeyValuePair<string, string>("ME", "Maine" )); arrList.Add(new KeyValuePair<string, string>("MD", "Maryland" )); arrList.Add(new KeyValuePair<string, string>("MA", "Massachusetts" )); arrList.Add(new KeyValuePair<string, string>("MI", "Michigan" )); arrList.Add(new KeyValuePair<string, string>("MN", "Minnesota" )); arrList.Add(new KeyValuePair<string, string>("MS", "Mississippi" )); arrList.Add(new KeyValuePair<string, string>("MO", "Missouri" )); arrList.Add(new KeyValuePair<string, string>("MT", "Montana" )); arrList.Add(new KeyValuePair<string, string>("NE", "Nebraska" )); arrList.Add(new KeyValuePair<string, string>("NV", "Nevada" )); arrList.Add(new KeyValuePair<string, string>("NH", "New Hampshire" )); arrList.Add(new KeyValuePair<string, string>("NJ", "New Jersey" )); arrList.Add(new KeyValuePair<string, string>("NM", "New Mexico" )); arrList.Add(new KeyValuePair<string, string>("NY", "New York" )); arrList.Add(new KeyValuePair<string, string>("NC", "North Carolina" )); arrList.Add(new KeyValuePair<string, string>("ND", "North Dakota" )); arrList.Add(new KeyValuePair<string, string>("OH", "Ohio" )); arrList.Add(new KeyValuePair<string, string>("OK", "Oklahoma" )); arrList.Add(new KeyValuePair<string, string>("OR", "Oregon" )); arrList.Add(new KeyValuePair<string, string>("PA", "Pennsylvania" )); arrList.Add(new KeyValuePair<string, string>("RI", "Rhode Island" )); arrList.Add(new KeyValuePair<string, string>("SC", "South Carolina" )); arrList.Add(new KeyValuePair<string, string>("SD", "South Dakota" )); arrList.Add(new KeyValuePair<string, string>("TN", "Tennessee" )); arrList.Add(new KeyValuePair<string, string>("TX", "Texas" )); arrList.Add(new KeyValuePair<string, string>("UT", "Utah" )); arrList.Add(new KeyValuePair<string, string>("VT", "Vermont" )); arrList.Add(new KeyValuePair<string, string>("VA", "Virginia" )); arrList.Add(new KeyValuePair<string, string>("WA", "Washington" )); arrList.Add(new KeyValuePair<string, string>("WV", "West Virginia" )); arrList.Add(new KeyValuePair<string, string>("WI", "Wisconsin" )); arrList.Add(new KeyValuePair<string, string>("WY", "Wyoming" )); return arrList; } The extension method Html.CheckBoxFor () is intended for editing a property of a model of type boolean. You want to use it to select some objects from the IEnumerable collection. It is not right.
The right way:
in sight
<form action="/Customer/Process"> <% foreach (var customer in Model.Request.Customers) { %> <input type="checkbox" name="selectedId" value="<%= customer.id %>" /> <%= customer.name %> <br/> <% } %> <input type="submit"/> </form> in the controller
public string Process(IEnumerable<Guid> selectedId) { if (selectedId == null) { ModelState.AddModelError("selectedId", "Have to select at least one customer!"); return View(); } // do something with customers' ids } I used a helper class for this. it's pretty simple. Using the helper class, you can use the SelectList and put it in the helper, just like for a drop-down list.
in the helpers folder I have Checkboxlist.cs
using System; using System.Web.Mvc; using System.Collections.Generic; using System.Text; using System.Linq; namespace MVC2_NASTEST.Helpers { public static class CheckBoxListHelper { public static string CheckBoxList(this HtmlHelper helper, string name, IDictionary<string, string> items) { return CheckBoxList(helper, name, items, null, null); } public static string CheckBoxList(this HtmlHelper helper, string name, IDictionary<string, string> items, IDictionary<string, object> checkboxHtmlAttributes) { return CheckBoxList(helper, name, items, null, checkboxHtmlAttributes); } public static string CheckBoxList(this HtmlHelper helper, string name, IDictionary<string, string> items, IEnumerable<string> selectedValues) { return CheckBoxList(helper, name, items, selectedValues, null); } public static string CheckBoxList(this HtmlHelper helper, string name, IDictionary<string, string> items, IEnumerable<string> selectedValues, IDictionary<string, object> checkboxHtmlAttributes) { var selectListItems = from i in items select new SelectListItem { Text = i.Key, Value = i.Value, Selected = (selectedValues != null && selectedValues.Contains(i.Value)) }; return CheckBoxList(helper, name, selectListItems, checkboxHtmlAttributes); } public static string CheckBoxList(this HtmlHelper helper, string name, IEnumerable<SelectListItem> items) { return CheckBoxList(helper, name, items, null); } public static string CheckBoxList(this HtmlHelper helper, string name, IEnumerable<SelectListItem> items, IDictionary<string, object> checkboxHtmlAttributes) { var output = new StringBuilder(); foreach (var item in items) { output.Append("<div class=\"fields\"><label>"); var checkboxList = new TagBuilder("input"); checkboxList.MergeAttribute("type", "checkbox"); checkboxList.MergeAttribute("name", name); checkboxList.MergeAttribute("value", item.Value); // Check to see if it checked if (item.Selected) checkboxList.MergeAttribute("checked", "checked"); // Add any attributes if (checkboxHtmlAttributes != null) checkboxList.MergeAttributes(checkboxHtmlAttributes); checkboxList.SetInnerText(item.Text); output.Append(checkboxList.ToString(TagRenderMode.SelfClosing)); output.Append(" " + item.Text + "</label></div>"); } return output.ToString(); } } } code in my controller:
public static List<SelectListItem> lesgeverList() { return lesgeverList(-1); } public static List<SelectListItem> lesgeverList(int selectedID) { return lesgeverList(new int[] { selectedID }); } public static List<SelectListItem> lesgeverList(int[] lg) { NASDataContext _db = new NASDataContext(); var lesg = (from l in _db.Lesgevers where l.LG_Naam != "leeg" orderby l.LG_Naam, l.LG_Vnaam select l).ToSelectList(m => m.LG_Naam + " " + m.LG_Vnaam, m => m.LG_ID.ToString(), m => lg.Contains(m.LG_ID)); return lesg.ToList(); } // // GET: /Projectleiders/Create public ActionResult Create(int projID) { ViewData["projNaam"] = getProject(projID).Proj_Kortenaam; int[] keys = (from p in _db.ProjectleiderProjectens where p.Proj_ID == projID from l in _db.Lesgevers where p.LG_ID == l.LG_ID select l.LG_ID).ToArray(); ViewData["projleiders"] = MvcApplication.lesgeverList(keys); return toegankelijk(projID, null); } // // POST: /Projectleiders/Create [HttpPost] public ActionResult Create(FormCollection collection, int projID) { if (collection["lesgeverlist"] != null) { string[] lgevers = collection["lesgeverlist"].Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); List<ProjectleiderProjecten> lst = new List<ProjectleiderProjecten>(); foreach (string s in lgevers) { ProjectleiderProjecten prl = new ProjectleiderProjecten(); prl.LG_ID = int.Parse(s); prl.Proj_ID = projID; int count = (from m in _db.ProjectleiderProjectens where m.LG_ID == prl.LG_ID && m.Proj_ID == prl.Proj_ID select m).Count(); if (count <= 0) { //deze bestaat nog niet lst.Add(prl); } } //var test = _db.ProjectleiderProjectens.Where(p => p.Proj_ID == projID && !lgevers.Contains(p.LG_ID.ToString())).ToList(); _db.ProjectleiderProjectens.DeleteAllOnSubmit(_db.ProjectleiderProjectens.Where(p => p.Proj_ID == projID && !lgevers.Contains(p.LG_ID.ToString()))); _db.ProjectleiderProjectens.InsertAllOnSubmit(lst); _db.SubmitChanges(); return RedirectToAction("Index"); } else { ModelState.AddModelError("lesgeverlist", "Je hebt geen lesgevers geselecteerd"); ViewData["projleiders"] = MvcApplication.lesgeverList(); ViewData["projNaam"] = getProject(projID).Proj_Kortenaam; return View(); } } I am using the ToSelectList extension, which everyone should have.
public static class VSKOExtensions { public static IList<SelectListItem> ToSelectList<T>(this IEnumerable<T> itemsToMap, Func<T, string> textProperty, Func<T, string> valueProperty, Predicate<T> isSelected) { var result = new List<SelectListItem>(); foreach (var item in itemsToMap) { result.Add(new SelectListItem { Value = valueProperty(item), Text = textProperty(item), Selected = isSelected(item) }); } return result; } } the code in my creation view (which is also an editing view at the same time) is very simple
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MVC2_NASTEST.Models.ProjectleiderProjecten>" %> <%@ Import Namespace="MVC2_NASTEST.Helpers" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Create </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>Koppel projectleider voor <%= ViewData["projNaam"].ToString() %></h2> <% using (Html.BeginForm()) {%> <%= Html.ValidationSummary(true) %> <fieldset> <legend>Fields</legend> <div class="editor-label"> <%= Html.Label("Lesgevers")%> </div> <div class="editor-field"> <%= Html.CheckBoxList("Lesgeverlist", ViewData["projleiders"] as List<SelectListItem>)%> <%= Html.ValidationMessage("Lesgeverlist")%> </div> <p> <input type="submit" value="Create" /> </p> </fieldset> <% } %> <div> <%= Html.ActionLink("Back to List", "Index") %> </div> </asp:Content> how it works:
I get the values ββfrom the database of already selected users, get their identifier and pass it to the MvcApplication.lesgeverList method (keys);
then I get the favorites list back, put it in the viewdata, and the selected faces are selected in my view when I open it. then when I change some fields and save them, I check if the collection is not invalid (therefore something is selected), then I separate the variables that I return that are the "value that you give to SelectList. I go through them, I parse them into ints, get users from the database with their analyzed identifiers, with a counter I see if they exist or not in the "linked" table called _db.ProjectleiderProjectens
when all are added, I delete all the "unselected" 1 times using the linq operator
_db.ProjectleiderProjectens.DeleteAllOnSubmit(_db.ProjectleiderProjectens.Where(p => p.Proj_ID == projID && !lgevers.Contains(p.LG_ID.ToString()))); which I find pretty readable. delete all objects from, get all objects by their identifier and check which of them are not indicated in the line [] of the identifier
It works pretty well, I have to say. If you have a few more questions, just ask.
Just use client-side jQuery validation, and then double-check yourself on the server side, just make sure the form collection has a value.
There is nothing wrong with your for loop.