Как обрабатывать флажки в формах ASP.NET MVC?

246

Внимание: этому вопросу больше девяти лет!

Лучше всего искать новые вопросы или искать ответы ниже, чтобы найти конкретную версию 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)%>&nbsp;<%= 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 отображает его значение как «истинное ложное», а не просто истинное.

Очевидно, я что-то упустил. Я думаю, что это построено с мыслью о том, что объекты в коллекции, которые UpdateModel()обрабатываются в html-форме, обновляются с помощью или через ModelBinder.

Но мои объекты не созданы для этого; Значит ли это, что это единственный путь? Есть ли другой способ сделать это?

Уве Кейм
источник
2
Другие могут найти это решение полезным: stackoverflow.com/questions/3291501/…
Аарон

Ответы:

262

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, что, на мой взгляд, довольно неплохо.

Дилан Битти
источник
Ага! Это то, что я должен был сделать для своих приложений!
Adhip Gupta
70
WTF. скрытые поля ввода с тем же именем в качестве элемента управления. его ViewState 2.0!
Simon_Weaver
3
Это также присутствует в версии 1.0. Посмотрите на ответ @ andrea-balducci, который дает вам разумный способ справиться с этим. Если этот флажок не установлен, полученный текст должен быть «ложно ложным» - это хороший обходной путь для учета браузеров,
умирающих от разума
77
Сюрприз? WTF? Скрытый ввод имеет то же имя, что и флажок - если флажок с тем же именем не установлен, то его значение не публикуется, тогда как значение скрытого публикуется. Когда браузер в первый раз встречает именованный элемент, он использует это значение и игнорирует все остальные элементы с таким же именем. Это гарантирует отправку значения: true, если флажок установлен (при условии, что он находится над скрытым элементом), и false, если флажок не установлен (пустой флажок игнорируется, а скрытый становится отступом). Настоящий недоумение - вот почему никто не указал на это.
Неррага
19
@nerraga: Вы не правы: допустимо иметь несколько элементов формы с одинаковым именем; и если да, то все элементы публикуются, и я не уверен, что порядок на самом деле определен (хотя на практике, скорее всего, он будет просто в порядке страниц). Использование слова «ложь» в качестве значения несколько вводит в заблуждение, так что да, это WTF - лучшим, менее вводящим в заблуждение выбором было бы что-то вроде «существует» или какой-то бессмысленный токен, такой как тире.
Имон Нербонн
95

HtmlHelper добавляет скрытый ввод для уведомления контроллера о статусе Unchecked. Итак, чтобы иметь правильный проверенный статус:

bool bChecked = form[key].Contains("true");
Андреа Балдуччи
источник
1
Это самый простой подход к этой проблеме, и это то, что я всегда использую. Это позволяет вам использовать вспомогательные методы и учитывать поведение ASP.NET MVC.
Ник Дэниелс
8
.. или в случае не проверено: bool bChecked = form [key] .Equals ("false"); Выполнение .Contains ("false") завершается ошибкой, поскольку истинное значение равно "true, false".
Марк Робинсон
54

Если вам интересно, ПОЧЕМУ они вставили скрытое поле с тем же именем, что и флажок, причина заключается в следующем:

Комментарий из исходного кода MVCBetaSource \ MVC \ src \ MvcFutures \ Mvc \ ButtonsAndLinkExtensions.cs

Рендер дополнительный <input type="hidden".../>для флажков. Это относится к сценариям, в которых непроверенные флажки не отправляются в запросе. Отправка скрытого ввода позволяет узнать, что флажок присутствовал на странице при отправке запроса.

Я думаю, что за кулисами они должны знать это для привязки к параметрам методов действия контроллера. Я полагаю, вы могли бы иметь логическое значение в трех состояниях (связанное с параметром типа null). Я не пробовал это, но я надеюсь, что вот что они сделали.

Simon_Weaver
источник
4
Да, это удобно в сценариях, где у вас есть выгруженные сетки и т. Д., И вы хотите отменить выбор для элемента, который ранее был выбран в каком-либо бизнес-объекте.
RichardOD
49

Вы также должны использовать, <label for="checkbox1">Checkbox 1</label>потому что тогда люди могут нажать на текст метки, а также сам флажок. Его также легче стилизовать, и, по крайней мере, в IE он будет выделен при переходе по элементам управления страницы.

<%= Html.CheckBox("cbNewColors", true) %><label for="cbNewColors">New colors</label>

Это не просто «о, я мог бы сделать это». Это значительное улучшение пользовательского опыта. Даже если не все пользователи знают, что они могут нажать на ярлык, многие будут.

Simon_Weaver
источник
27

Я удивлен, что ни один из этих ответов не использовал встроенные функции 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)

Посетите мой блог для более подробной информации.

Шон Маклин
источник
1
Спасибо! Эвербойд все еще использует старые способы форм. И когда вы отправляете, вы можете получить доступ к Модели без всей этой коллекции [] и мусора request.form - это то, что я искал. +1и + пиво
Петр Кула
2
Это должно быть лучшим ответом на вопрос. Очень просто и легко реализовать.
Йохан Гунаван
Некоторое время пытался найти элегантное решение, прежде чем нашел ответ. Ему несколько лет, но он все еще в силе в 2016 году! При использовании в нескольких случаях используйте встроенный SelectListItem в качестве универсального типа, который идеально подходит для этого
Фил
Является ли SampleObject списком? Например: @ModelType List (Of Type)
JoshYates1980
25

Вот что я делал.

Посмотреть:


<input type="checkbox" name="applyChanges" />

контроллер:


var checkBox = Request.Form["applyChanges"];

if (checkBox == "on")
{
...
}

Я нашел вспомогательные методы Html. * В некоторых случаях не очень полезными, и мне было бы лучше делать это в простом старом HTML. Это один из них, другой, который приходит на ум, это радио кнопки.

Изменить: это в Preview 5, очевидно, YMMV между версиями.

mmacaulay
источник
1
Я просто хочу добавить альтернативный пример: Object.Property =! String.IsNullOrEmpty (Request.Form ["NAME"]);
Gup3rSuR4c
если не
отмечено,
10

Похоже, что они выбирают чтение только первого значения, поэтому это «true», когда флажок установлен, и «false», когда включено только скрытое значение. Это легко получить с помощью следующего кода:

model.Property = collection["ElementId"].ToLower().StartsWith("true");
пушистый
источник
8

@ Дилан Битти Отличная находка !!! Большое спасибо Чтобы расширить еще больше, этот метод также прекрасно работает с подходом 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!

nautic20
источник
Спасибо, ваш SampleViewModel прояснил некоторую путаницу в исходном ответе.
парламент
6

Я также хотел бы отметить, что вы можете присвоить каждому флажку свое имя и включить его в параметры 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) {
    ...
}

Значения из представления передаются действию, поскольку имена совпадают.

Я знаю, что это решение не идеально подходит для вашего проекта, но я подумал, что я бы предложил эту идею.

Darcy
источник
5
<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";
         }
    }
bluwater2001
источник
4

Эта проблема также возникает в выпуске 1.0. Html.Checkbox () вызывает добавление еще одного скрытого поля с тем же именем / идентификатором, что и у исходного флажка. И когда я пытался загрузить массив флажков с помощью document.GetElemtentsByName (), вы можете догадаться, как все было испорчено. Это странно.

Фархан Зия
источник
4

Из того, что я могу собрать, модель не хочет угадывать, был ли флажок = true или false, я обошел это, установив атрибут value для элемента checkbox с помощью jQuery перед отправкой формы следующим образом:

 $('input[type="checkbox"]').each(function () {
       $(this).attr('value', $(this).is(':checked'));
 }); 

Таким образом, вам не нужен скрытый элемент только для хранения значения флажка.

BraveNewMath
источник
4

Я знаю, что этот вопрос был написан, когда 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)

но когда вы пытаетесь динамически построить эти списки и проверить значения, вы никогда не узнаете Id во время компиляции, поэтому вам придется обрабатывать их на лету.

Дэн ВанВинкл
источник
есть ли причина, по которой вы используете Request.Params вместо добавления параметра FormCollection в метод?
SwissCoder
Нет причин, я не вижу никакой пользы от одного способа перед другим.
Дэн ВанВинкл
1
Извините, на самом деле есть причина. Просто посмотрел на мой код, и я использую этот один метод для 5 различных вызовов, и я не чувствовал, что передавал FormCollection из каждого метода. Это был самый простой способ получить ключи без необходимости что-либо передавать.
Дэн ВанВинкл
да, я новичок в MVC. И я где-то читал, что в общем случае лучше использовать FormCollection вместо Request.Params, так же, как использовать типизированную модель вместо FormCollection (поэтому на самом деле было бы лучше использовать модель и избегать использования Request.Params в целом). Я заметил, что другие элементы кода не используются, такие как переменная allPermissions и keyAsInt. Спасибо за ответы.
SwissCoder
3

Самый простой способ сделать это так ...

Вы устанавливаете имя и значение.

<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);
        }
кк-dev11
источник
3

То же, что и ответ 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>
<% } %>
ChinaHelloWorld
источник
Спасибо за ответ, но этот вопрос немного устарел . Шесть лет, о. Вы можете отредактировать и указать, какую версию MVC вы используете. Это поможет любому, кто использует более новую версию, найти этот ответ.
Я второй это. Удаление уникального удостоверения личности решило эту проблему после сильных головных болей и скрежета зубов
Оди
Версия MVC, которую я использую, - .net MVC 4.0 для этой проблемы.
ChinaHelloWorld
2

это то, что я сделал, чтобы потерять двойные значения при использовании Html.CheckBox (...

Replace("true,false","true").Split(',')

с 4-мя флажками, не отмеченными, не проверенными, проверенными, становится ли true, false, false, false, true, false в true, false, false, true. только то, что мне нужно

Йерун
источник
0

Как насчет этого?

bool isChecked = false;
if (Boolean.TryParse(Request.Form.GetValues(”chkHuman”)[0], out isChecked) == false)
    ModelState.AddModelError(”chkHuman”, Nice try.”);
Skadoosh
источник
0

При использовании флажка 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как это возвращает в виде массива.

eyesnz
источник
0

Просто сделайте это на $(document).ready:

$('input:hidden').each(function(el) {
    var that = $(this)[0];
    if(that.id.length < 1 ) {

        console.log(that.id);
        that.parentElement.removeChild(that);

    }
});
doronAv
источник
2
К сожалению, вы можете получить странные результаты на стороне сервера для людей с отключенным JavaScript (плагин NoScript, старые мобильные телефоны, Lynx ...)
ArIck
0

Мое решение:

<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- действительный-логический /

pawlom84
источник
Вам не нужно использовать какой-либо Javascript для этого ... var isChecked = formData [key] .Contains ("true");
Jammer
0

У меня была почти такая же проблема, но возвращаемое значение моего контроллера было заблокировано другими значениями.

Нашел простое решение, но оно кажется немного грубым.

Попробуйте ввести 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()" />
treborian
источник
0

Используя @mmacaulay, я придумал это для bool:

// MVC Work around for checkboxes.
bool active = (Request.Form["active"] == "on");

Если отмечено активно = true

Если флажок не активен = false

Рави Рам
источник