Как обрабатывать флажки в формах ASP.NET MVC?
Внимание: этому вопросу больше девяти лет!
Лучше всего искать новые вопросы или искать ответы ниже, чтобы найти конкретную версию MVC, так как многие ответы здесь устарели.
Если вы найдете ответ, который подходит для вашей версии, убедитесь, что он содержит версию MVC, которую вы используете.
(Оригинальный вопрос начинается ниже)
Мне это кажется немного странным, но, насколько я могу судить, именно так вы и делаете.
У меня есть коллекция объектов, и я хочу, чтобы пользователи выбирали один или несколько из них. Это говорит мне "форма с флажками". Мои объекты не имеют никакого понятия "выбранный" (они элементарные POCO, сформированные путем десериализации вызова wcf). Итак, я делаю следующее:
public class SampleObject{
public Guid Id {get;set;}
public string Name {get;set;}
}
По мнению:
<%
using (Html.BeginForm())
{
%>
<%foreach (var o in ViewData.Model) {%>
<%=Html.CheckBox(o.Id)%> <%= o.Name %>
<%}%>
<input type="submit" value="Submit" />
<%}%>
И в контроллере это единственный способ увидеть, какие объекты проверял пользователь:
public ActionResult ThisLooksWeird(FormCollection result)
{
var winnars = from x in result.AllKeys
where result[x] != "false"
select x;
// yadda
}
Во-первых, это причудливо, а во-вторых, для тех элементов, которые проверял пользователь, FormCollection отображает его значение как "истинное ложное", а не просто истинное.
Очевидно, я что-то упустил. Я думаю, что это построено с учетом того, что объекты в коллекции, которые обрабатываются в HTML-форме, обновляются с помощью UpdateModel()
или через ModelBinder.
Но мои объекты не созданы для этого; Значит ли это, что это единственный путь? Есть ли другой способ сделать это?
22 ответа
Html.CheckBox делает что-то странное - если вы посмотрите на исходную страницу, вы увидите, что <input type="hidden" />
создается рядом с каждым флажком, который объясняет "истинные ложные" значения, которые вы видите для каждого элемента формы.
Попробуйте это, что определенно работает на ASP.NET MVC Beta, потому что я только что попробовал.
Поместите это в представление вместо использования Html.CheckBox():
<% using (Html.BeginForm("ShowData", "Home")) { %>
<% foreach (var o in ViewData.Model) { %>
<input type="checkbox" name="selectedObjects" value="<%=o.Id%>">
<%= o.Name %>
<%}%>
<input type="submit" value="Submit" />
<%}%>
Ваши флажки все называются selectedObjects
и value
каждого флажка - GUID соответствующего объекта.
Затем опубликуйте следующее действие контроллера (или нечто подобное, что делает что-то полезное вместо Response.Write())
public ActionResult ShowData(Guid[] selectedObjects) {
foreach (Guid guid in selectedObjects) {
Response.Write(guid.ToString());
}
Response.End();
return (new EmptyResult());
}
Этот пример просто напишет GUID блоков, которые вы отметили; ASP.NET MVC отображает значения GUID выбранных флажков в Guid[] selectedObjects
параметр, и даже анализирует строки из коллекции Request.Form в экземпляры объектов GUID, что, на мой взгляд, довольно неплохо.
HtmlHelper добавляет скрытый ввод для уведомления контроллера о статусе Unchecked. Итак, чтобы иметь правильный проверенный статус:
bool bChecked = form[key].Contains("true");
Если вам интересно, ПОЧЕМУ они вставили скрытое поле с тем же именем, что и флажок, причина заключается в следующем:
Комментарий из исходного кода MVCBetaSource \ MVC \ src \ MvcFutures \ Mvc \ButtonsAndLinkExtensions.cs
Сделать дополнительный
<input type="hidden".../>
для флажков. Это относится к сценариям, в которых непроверенные флажки не отправляются в запросе. Отправка скрытого ввода позволяет узнать, что флажок присутствовал на странице при отправке запроса.
Я думаю, что за кулисами они должны знать это для привязки к параметрам методов действия контроллера. Полагаю, вы могли бы иметь логическое значение с тремя состояниями (связанное с параметром bool). Я не пробовал это, но я надеюсь, что вот что они сделали.
Вы также должны использовать <label for="checkbox1">Checkbox 1</label>
потому что тогда люди могут нажать на текст метки, а также сам флажок. Его также легче стилизовать, и, по крайней мере, в IE он будет выделен при переходе по элементам управления страницы.
<%= Html.CheckBox("cbNewColors", true) %><label for="cbNewColors">New colors</label>
Это не просто "о, я мог бы сделать это". Это значительное улучшение пользовательского опыта. Даже если не все пользователи знают, что они могут нажать на ярлык, многие будут.
Я удивлен, что ни один из этих ответов не использовал встроенные функции MVC для этого.
Я написал пост в блоге об этом здесь, который даже на самом деле связывает ярлыки с флажком. Я использовал папку EditorTemplate, чтобы сделать это простым и модульным способом.
Вы просто получите новый файл в папке EditorTemplate, который выглядит следующим образом:
@model SampleObject
@Html.CheckBoxFor(m => m.IsChecked)
@Html.HiddenFor(m => m.Id)
@Html.LabelFor(m => m.IsChecked, Model.Id)
на самом деле, вам не нужно будет зацикливаться, просто 1 строка кода:
@Html.EditorFor(x => ViewData.Model)
Посетите мой блог для более подробной информации.
Вот что я делал.
Посмотреть:
<input type="checkbox" name="applyChanges" />
контроллер:
var checkBox = Request.Form["applyChanges"];
if (checkBox == "on")
{
...
}
Я нашел вспомогательные методы Html.* В некоторых случаях не очень полезными, и мне было бы лучше делать это в простом старом HTML. Это один из них, другой, который приходит на ум, это радио кнопки.
Изменить: это в Preview 5, очевидно, YMMV между версиями.
Похоже, что они выбирают чтение только первого значения, поэтому это "true", когда флажок установлен, и "false", когда включено только скрытое значение. Это легко получить с помощью следующего кода:
model.Property = collection["ElementId"].ToLower().StartsWith("true");
@ Дилан Битти Отличная находка!!! Большое спасибо Чтобы еще больше расширить этот метод, он также прекрасно работает с подходом View Model. MVC настолько крут, что достаточно умен, чтобы связать массив Guids со свойством с тем же именем объекта Model, привязанного к View. Пример:
ViewModel:
public class SampleViewModel
{
public IList<SampleObject> SampleObjectList { get; set; }
public Guid[] SelectedObjectIds { get; set; }
public class SampleObject
{
public Guid Id { get; set; }
public string Name { get; set; }
}
}
Посмотреть:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Sample View</h2>
<table>
<thead>
<tr>
<th>Checked</th>
<th>Object Name</th>
</tr>
</thead>
<% using (Html.BeginForm()) %>
<%{%>
<tbody>
<% foreach (var item in Model.SampleObjectList)
{ %>
<tr>
<td><input type="checkbox" name="SelectedObjectIds" value="<%= item.Id%>" /></td>
<td><%= Html.Encode(item.Name)%></td>
</tr>
<% } %>
</tbody>
</table>
<input type="submit" value="Submit" />
<%}%>
контроллер:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult SampleView(Guid id)
{
//Object to pass any input objects to the View Model Builder
BuilderIO viewModelBuilderInput = new BuilderIO();
//The View Model Builder is a conglomerate of repositories and methods used to Construct a View Model out of Business Objects
SampleViewModel viewModel = sampleViewModelBuilder.Build(viewModelBuilderInput);
return View("SampleView", viewModel);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SampleView(SampleViewModel viewModel)
{
// The array of Guids successfully bound to the SelectedObjectIds property of the View Model!
return View();
}
Любой, кто знаком с философией View Model, будет рад, это работает как Champ!
Я также хотел бы отметить, что вы можете присвоить каждому флажку свое имя и включить его в параметры actionresults.
Пример,
Посмотреть:
<%= Html.CheckBox("Rs232CheckBox", false, new { @id = "rs232" })%>RS-232
<%= Html.CheckBox("Rs422CheckBox", false, new { @id = "rs422" })%>RS-422
контроллер:
public ActionResults MyAction(bool Rs232CheckBox, bool Rs422CheckBox) {
...
}
Значения из представления передаются действию, поскольку имена совпадают.
Я знаю, что это решение не идеально подходит для вашего проекта, но я подумал, что я бы предложил эту идею.
<input type = "checkbox" name = "checkbox1" /> <label> Check to say hi.</label>
От контроллера:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection fc)
{
var s = fc["checkbox1"];
if (s == "on")
{
string x = "Hi";
}
}
Эта проблема также возникает в выпуске 1.0. Html.Checkbox() вызывает добавление еще одного скрытого поля с тем же именем / идентификатором, что и у исходного флажка. И когда я пытался загрузить массив флажков с помощью document.GetElemtentsByName(), вы можете догадаться, как все это испортилось. Это странно.
Я знаю, что этот вопрос был написан, когда MVC3 не было, но для любого, кто приходит к этому вопросу и использует MVC3, вы можете захотеть "правильный" способ сделать это.
Пока думаю, что занимаюсь всем
Contains("true");
вещь отличная и чистая, и работает на всех версиях MVC, проблема в том, что она не учитывает культуру (как будто это действительно имеет значение в случае bool).
"Правильный" способ выяснить значение bool, по крайней мере в MVC3, заключается в использовании ValueProvider.
var value = (bool)ValueProvider.GetValue("key").ConvertTo(typeof(bool));
Я делаю это на одном из сайтов моего клиента при редактировании разрешений:
var allPermissionsBase = Request.Params.AllKeys.Where(x => x.Contains("permission_")).ToList();
var allPermissions = new List<KeyValuePair<int, bool>>();
foreach (var key in allPermissionsBase)
{
// Try to parse the key as int
int keyAsInt;
int.TryParse(key.Replace("permission_", ""), out keyAsInt);
// Try to get the value as bool
var value = (bool)ValueProvider.GetValue(key).ConvertTo(typeof(bool));
}
Теперь, прелесть этого в том, что вы можете использовать это с любым простым типом, и это будет даже правильно на основе культуры (например, деньги, десятичные дроби и т. Д.).
ValueProvider - это то, что используется при формировании ваших действий следующим образом:
public ActionResult UpdatePermissions(bool permission_1, bool permission_2)
но когда вы пытаетесь динамически построить эти списки и проверить значения, вы никогда не узнаете I d во время компиляции, поэтому вам придется обрабатывать их на лету.
Из того, что я могу собрать, модель не хочет угадывать, был ли флажок = true или false, я обошел это, установив атрибут value в элементе checkbox с помощью jQuery перед отправкой формы следующим образом:
$('input[type="checkbox"]').each(function () {
$(this).attr('value', $(this).is(':checked'));
});
Таким образом, вам не нужен скрытый элемент, чтобы просто сохранить значение флажка.
То же, что и ответ nautic20, просто используйте список флажков привязки модели MVC по умолчанию с тем же именем, что и у свойства коллекции string / int / enum во ViewModel. Вот и все.
Но одну проблему нужно указать. В каждом компоненте флажка не следует указывать "Id", который повлияет на привязку модели MVC.
Следующий код будет работать для привязки модели:
<% foreach (var item in Model.SampleObjectList)
{ %>
<tr>
<td><input type="checkbox" name="SelectedObjectIds" value="<%= item.Id%>" /></td>
<td><%= Html.Encode(item.Name)%></td>
</tr>
<% } %>
Следующие коды не будут привязаны к модели (разница здесь в том, что для каждого флажка назначен идентификатор)
<% foreach (var item in Model.SampleObjectList)
{ %>
<tr>
<td><input type="checkbox" name="SelectedObjectIds" id="[some unique key]" value="<%= item.Id%>" /></td>
<td><%= Html.Encode(item.Name)%></td>
</tr>
<% } %>
Самый простой способ сделать это так...
Вы устанавливаете имя и значение.
<input type="checkbox" name="selectedProducts" value="@item.ProductId" />@item.Name
Затем при отправке захватите значения флажков и сохраните их в массиве int. тогда соответствующая функция LinQ. Это оно..
[HttpPost]
public ActionResult Checkbox(int[] selectedObjects)
{
var selected = from x in selectedObjects
from y in db
where y.ObjectId == x
select y;
return View(selected);
}
Это то, что я сделал, чтобы потерять двойные значения при использовании Html.CheckBox(...
Replace("true,false","true").Split(',')
с 4-мя флажками, не отмеченными, не проверенными, проверенными, становится ли истина, ложь, ложь, ложь, истина, ложь в истину, ложь, ложь, истина. только то, что мне нужно
У меня была почти такая же проблема, но возвращаемое значение моего контроллера было заблокировано другими значениями.
Нашел простое решение, но оно кажется немного грубым.
Попробуй набрать Viewbag.
в вашем контроллере, и теперь вы даете ему имя, как Viewbag.Checkbool
Теперь переключитесь на View и попробуйте это @Viewbag.Checkbool
с этим вы получите значение из контроллера.
Параметры моего контроллера выглядят так:
public ActionResult Anzeigen(int productid = 90, bool islive = true)
и мой флажок будет обновляться так:
<input id="isLive" type="checkbox" checked="@ViewBag.Value" ONCLICK="window.location.href = '/MixCategory/Anzeigen?isLive=' + isLive.checked.toString()" />
Мое решение:
<input type="checkbox" id="IsNew-checkbox" checked="checked" />
<input type="hidden" id="IsNew" name="IsNew" value="true" />
<script language="javascript" type="text/javascript" >
$('#IsNew-checkbox').click(function () {
if ($('#IsNew-checkbox').is(':checked')) {
$('#IsNew').val('true');
} else {
$('#IsNew').val('false');
}
});
</script>
Больше вы можете найти здесь: http://www.blog.mieten.pl/2010/12/asp-net-mvc-custom-checkbox-as-solution-of-string-was-not-recognized-as-a-valid-boolean/
Используя @mmacaulay, я придумал это для bool:
// MVC Work around for checkboxes.
bool active = (Request.Form["active"] == "on");
Если отмечено активно = true
Если флажок не активен = false
При использовании флажка HtmlHelper я предпочитаю работать с опубликованными данными формы флажка в виде массива. Я действительно не знаю почему, я знаю, что другие методы работают, но я думаю, что я просто предпочитаю рассматривать строки, разделенные запятыми, как массив, насколько это возможно.
Таким образом, выполнение проверенного или истинного теста будет:
//looking for [true],[false]
bool isChecked = form.GetValues(key).Contains("true");
Ложная проверка будет:
//looking for [false],[false] or [false]
bool isNotChecked = !form.GetValues(key).Contains("true");
Основное отличие заключается в использовании GetValues
как это возвращает в виде массива.
Как насчет этого?
bool isChecked = false;
if (Boolean.TryParse(Request.Form.GetValues(”chkHuman”)[0], out isChecked) == false)
ModelState.AddModelError(”chkHuman”, “Nice try.”);
Просто сделай это на $(document).ready
:
$('input:hidden').each(function(el) {
var that = $(this)[0];
if(that.id.length < 1 ) {
console.log(that.id);
that.parentElement.removeChild(that);
}
});