How to pass IEnumerable list to controller in MVC including checkbox state?
Use a list instead and replace your foreach
loop with a for
loop:
@model IList<BlockedIPViewModel>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
@Html.HiddenFor(x => x[i].IP)
@Html.CheckBoxFor(x => x[i].Checked)
</td>
<td>
@Html.DisplayFor(x => x[i].IP)
</td>
</tr>
}
<div>
<input type="submit" value="Unblock IPs" />
</div>
}
Alternatively you could use an editor template:
@model IEnumerable<BlockedIPViewModel>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.EditorForModel()
<div>
<input type="submit" value="Unblock IPs" />
</div>
}
and then define the template ~/Views/Shared/EditorTemplates/BlockedIPViewModel.cshtml
which will automatically be rendered for each element of the collection:
@model BlockedIPViewModel
<tr>
<td>
@Html.HiddenFor(x => x.IP)
@Html.CheckBoxFor(x => x.Checked)
</td>
<td>
@Html.DisplayFor(x => x.IP)
</td>
</tr>
The reason you were getting null in your controller is because you didn't respect the naming convention for your input fields that the default model binder expects to successfully bind to a list. I invite you to read the following article
.
Once you have read it, look at the generated HTML (and more specifically the names of the input fields) with my example and yours. Then compare and you will understand why yours doesn't work.
Pass List of Checkboxes into View and Pull out IEnumerable
You cannot bind to a collection using a foreach
loop. Nor should you be manually generating your html, which in this case would not work because unchecked checkboxes do not post back. Always use the strongly typed html helpers so you get correct 2-way model binding.
You have not indicated what you models are, but assuming you have a User
and want to select Roles
for that user, then create view models to represent what you want to display in the view
public class RoleVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class UserVM
{
public UserVM()
{
Roles = new List<RoleVM>();
}
public int ID { get; set; }
public string Name { get; set; }
public List<RoleVM> Roles { get; set; }
}
In the GET method
public ActionResult Edit(int ID)
{
UserVM model = new UserVM();
// Get you User based on the ID and map properties to the view model
// including populating the Roles and setting their IsSelect property
// based on existing roles
return View(model);
}
View
@model UserVM
@using(Html.BeginForm())
{
@Html.HiddenFor(m => m.ID)
@Html.DisplayFor(m => m.Name)
for(int i = 0; i < Model.Roles.Count; i++)
{
@Html.HiddenFor(m => m.Roles[i].ID)
@Html.CheckBoxFor(m => m.Roles[i].IsSelected)
@Html.LabelFor(m => m.Roles[i].IsSelected, Model.Roles[i].Name)
}
<input type"submit" />
}
Then in the post method, your model will be bound and you can check which roles have been selected
[HttpPost]
public ActionResult Edit(UserVM model)
{
// Loop through model.Roles and check the IsSelected property
}
Pass IEnumerable to View and Display It
After researching and learning more about Razor, I figured the easiest way to accomplish this was to create a view model containing an IEnumerator<Cars>
, a SelectedItem SelectedCar
and OwnerModel Car
. I then initialized the new view model in the controller and was able to pass along the data.
Passing IEnumerable property of model to controller post action- ASP MVC
The context of the model in the view is from the model being passed to it.
E.g. by passing
Model.Filter.CustomsitemDetails[i]
to your partial, you have changed the root from the Model object to your CustomiseDetails object. (so the name of the input will be "CustomsItemClassificationID" instead of "Filter.CustomsitemDetails[i].CustomsItemClassificationID", and so will not be rebound when posted, as will no longer match the property it is being bound to.
The correct way to do this is to use an Editor Template. This will preserve the model's origin. To do this, Create a folder called EditorTemplates under Views > Shared.
Copy your partial into it, and rename it to be called the name of your type. For example, if the type of Model.Filter.CustomsitemDetails[i] is CustomsitemDetail then your file under the editor template folder will be called CustomsitemDetails.cshtml.
Now, instead of
@Html.RenderPartial("CustomsItemDetailsUserControl", Model.Filter.CustomsitemDetails[i]);
Use
@Html.EditorFor(m => m.Filter.CustomitemDetails[i])
Alternatively, if you don't want to rename or move your file, you can specify it's current location instead:
@Html.EditorFor(m => m.Filter.CustomitemDetails[i], "~/Views/{controller}/CustomsItemDetailsUserControl.cshtml")
UPDATE
So I've learnt something new:
If a template whose name matches the templateName parameter is found in the controller's EditorTemplates folder, that template is used to render the expression. If a template is not found in the controller's EditorTemplates folder, the Views\Shared\EditorTemplates folder is searched for a template that matches the name of the templateName parameter. If no template is found, the default template is used. from MSDN.
So contrary to the second part of my answer, you do need to move your partial to
"~/Areas/TaxCalculation/Views/Home/EditorTemplates/CustomsItemDetailsUserControl.cshtml"
And use:
@Html.EditorFor(m => m.Filter.CustomitemDetails[i], "CustomsItemDetailsUserControl")
UPDATE 2
As per Stephen's comment - EditorFor() has an overload that accepts IEnumerable so you can just use
@Html.EditorFor(m => m.Filter.CustomitemDetails, "CustomsItemDetailsUserControl")
Or if you have an EditorTemplate named CustomitemDetails.cshtml located in the /Views/Shared/EditorTemplates/ folder:
@Html.EditorFor(m => m.Filter.CustomitemDetails)
pass value from MVC View to MVC Controller depending on checkbox checked
Your generating checkboxes with name="code"
therefore you POST method signature needs to be
public ActionResult ShowRecords(int[] code)
The code
parameter will contain an array of the values of the checked checkboxes.
Based on your edit using a view model, your view will need to use a for
loop or custom EditorTemplate
so that the name
attributes are generated correctly for binding to a collection (refer Post an HTML Table to ADO.NET DataTable for more detail)
@for (int i = 0; i < Model.Count; i++)
{
<td>
@Html.CheckBoxFor(m => m[i].code) // include hidden input so its submitted
@Html.DisplayFor(m=> m[i].code)
</td>
....
<td>@Html.CheckBoxFor(m => m[i].isChecked)</td>
}
and the POST method signature would be
public ActionResult ShowRecords(List<Developer> model)
how can i pass (save) a list from a view to an controller?
Assuming you have IEnumerable<TRecord>
as model bound to view, you can change foreach
loop to for
loop with index array of every properties inside model and set binding type to IList
to match with controller input parameter:
@model IList<TRecord>
@using (Html.BeginForm())
{
// display names area
@for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
@Html.DisplayFor(item => item[i].Tid)
</td>
<td>
@Html.EditFor(item => item[i].Tname)
</td>
</tr>
}
// submit button area
}
The other way to post IEnumerable
collection is creating editor template and put HTML helpers inside it, then use EditorForModel
(see Darin Dimitrov's approach here).
Similar issues:
How to pass IEnumerable list to controller in MVC including checkbox state?
MVC passing IEnumerable<CustomVM> to Controller
Pass IEnumerable list to controller
MVC Pass IDs from view to controller from checkbox multiselection
There are probably other ways to do this, but I have done it like this previously - Create a new Checkbox class like:
public class CheckboxModel
{
//Value of checkbox
public int Value { get; set; }
//description of checkbox
public string Text { get; set; }
//whether the checkbox is selected or not
public bool IsChecked { get; set; }
}
Initialise static list of users, to give you an idea (you probably have to generate it dynamically):
ListOfUserID = new List<CheckboxModel>
{
new CheckboxModel { Value = 1, Text = "User1" },
new CheckboxModel { Value = 2, Text = "User2" },
new CheckboxModel { Value = 3, Text = "User3" }
};
Use this class in the view (for example in a loop):
@Html.CheckBoxFor(m => Model.ListOfUserID[i].IsChecked)@Model.ListOfUserID[i].Text
@Html.HiddenFor(m => Model.ListOfUserID[i].Value)
@Html.HiddenFor(m => Model.ListOfUserID[i].Text)
Then you have the text or value of the checkbox in Controller action when the form is posted.
How to use Html.CheckBox (list) with IEnumerable T with validation
You could create a custom html extensions class and overload the CheckBoxFor method like below. The method evaluates the metadata.Model to the value passed into it (like U.S. State). You can get the checkbox value/s from the FormCollection in the ControllerAction:
public ActionResult Edit(FormCollection formCollection)
{
// Get the value(s)
string checkBox = formCollection["State"];
// perform validation
....
}
Example assumes a keyvaluepair generic list
<% 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'm at it, here's my list of United States to complete code:
/// <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;
}
Related Topics
Blazor - Display Wait or Spinner on API Call
Regular Expression Groups in C#
Is the Order of Static Class Initialization in C# Deterministic
How to Find Control in Templatefield of Gridview
Why Does This Floating-Point Calculation Give Different Results on Different MAChines
General Purpose Fromevent Method
Why Can't I Unbox an Int as a Decimal
Custom Text Color in C# Console Application
How to Bind an Enum to a Combobox Control in Wpf
Correct Use of Multimapping in Dapper
Referencing a Variable from Another Method
Changing the Value of an Element in a List of Structs
Cannot Use Ref or Out Parameter in Lambda Expressions
Best Way to Repeat a Character in C#
.Net/C# - Convert Char[] to String
Impossible to Use Ref and Out for First ("This") Parameter in Extension Methods