Як обробити прапорці у формах 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 перераховує його значення як "справжнє помилкове", а не просто істинне.

Очевидно, мені щось не вистачає. Я думаю, що це побудовано з ідеєю на увазі, що об'єкти колекції, на які діє у формі html, оновлюються за допомогою UpdateModel()або через 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. приховані поля введення з іменем SAME як контролем. його ViewState 2.0!
Simon_Weaver

3
Це також присутнє у версії 1.0. Подивіться на подану відповідь @ andrea-balducci, яка дає вам розумний спосіб впоратися з цим. Якщо прапорець не встановлений, отриманий текст повинен бути "помилковим помилковим" - це гарне рішення для обліку браузерних мертвих браузерів ...
Брайан Ребейн

77
Сюрприз? Wtf? Прихований вхід має те саме ім'я, що і прапорець - якщо прапорець з тим самим іменем не встановлений, його значення не розміщується, тоді як значення прихованого розміщується. Перший раз, коли браузер стикається з названим елементом, він буде використовувати це значення та ігнорувати всі інші елементи з тим самим іменем. Це гарантує подання значення: true, якщо прапорець встановлений (припускаючи, що він знайдений над прихованим елементом) та false, якщо прапорець не встановлений (порожній прапорець ігнорується, а прихований стає назад). Справжній wtf, чому ніхто інший не зазначив цього.
nerraga

19
@nerraga: Ви помиляєтеся: допустимо html мати декілька елементів форми з однаковою назвою; і якщо так, всі елементи розміщуються, і я не впевнений, що порядок дійсно визначений (хоча, напевно, це просто в порядку порядку на сторінці). Використання слова "false" як значення дещо вводить в оману, так що так, це WTF - кращий, менш оманливий вибір був би чимось на кшталт "існує" або якийсь безглуздий маркер, як тире.
Еймон Нербонна

95

HtmlHelper додає прихований вхід, щоб повідомити контролеру про стан без перевірки. Отже, щоб мати правильний перевірений статус:

bool bChecked = form[key].Contains("true");

1
Це найпростіший підхід до цієї проблеми, і це я завжди використовую. Це дозволяє використовувати допоміжні методи та враховувати поведінку MVC ASP.NET.
Нік Даніельс

8
.. або у випадку, якщо не перевірено: bool bChecked = form [key] .Equals ("false"); Виконувати.
Марк Робінсон

54

Якщо вам цікаво, Чому вони ставлять приховане поле з тим же ім'ям, що і прапорець, причина полягає в наступному:

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

Надати додатковий <input type="hidden".../> прапорці. Це адресує сценарії, коли в запиті не надсилаються відмічені прапорці. Надсилання прихованого вводу дає змогу дізнатися, що прапорець присутній на сторінці під час подання запиту.

Я думаю, що за лаштунками вони повинні знати це для прив’язки до параметрів методів дії контролера. Тоді ви могли б мати тридержавний булевий сигнал, я вважаю (прив’язаний до нульового параметра bool). Я не пробував цього, але сподіваюся, що вони зробили.


4
Так, це зручно в сценаріях, коли ви підключили сітки і т. Д., І ви хочете зняти вибір для елемента, який був раніше обраний у якомусь бізнес-об'єкті.
RichardOD

49

Ви також повинні використовувати, <label for="checkbox1">Checkbox 1</label>тому що тоді люди можуть натискати на текст етикетки, а також сам прапорець. Його також простіше в стилі, і принаймні в IE це буде виділено під час перегляду елементів управління на сторінці.

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

Це не просто "о, я міг би це зробити". Це суттєве вдосконалення роботи користувачів. Навіть якщо не всі користувачі знають, що вони можуть натиснути на етикетку, багато хто буде.


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 як загальний тип, який ідеально підходить для цього
Phil

Чи є SampleObject список? Такі як: @ModelType List (типу)
JoshYates1980

25

Ось що я робив.

Вид:


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

Контролер:


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

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

Я знайшов допоміжні методи Html. * В деяких випадках не настільки корисними, і мені було краще робити це у звичайному старому HTML. Це одна з них, інша, яка спадає на думку, - це кнопки радіо.

Редагувати: це в Preview 5, очевидно, YMMV між версіями.


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

якщо це не встановлено, це переходить до винятку
Xulfee

10

Здається, вони вирішують читати лише перше значення, тому це "вірно", коли прапорець встановлено, і "помилково", коли включено лише приховане значення. Це легко отримати такий код:

model.Property = collection["ElementId"].ToLower().StartsWith("true");

8

@Dylan Beattie Чудова знахідка !!! Я дякую вам дуже Щоб ще більше розширитись, ця методика також ідеально підходить із підходом View Model. MVC настільки крутий, що він досить розумний, щоб прив’язати масив Guids до властивості тим самим іменем об'єкта Model, прив'язаного до виду. Приклад:

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, зрадіють, це працює як Чемпіон!


Дякуємо, ваш SampleViewModel очистив деяку плутанину в оригінальній відповіді.
парламент

6

Я також хотів би зазначити, що ви можете назвати кожен прапорець іншим ім'ям і мати це ім'я частиною параметрів результатів дій.

Приклад,

Вид:

 <%= 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) {
    ...
}

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

Я знаю, що це рішення не є ідеальним для вашого проекту, але я думав, що я викину цю ідею.


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";
         }
    }

4

Ця проблема також відбувається у версії 1.0. Html.Checkbox () спричиняє додавання іншого прихованого поля з тим самим іменем / ідентифікатором, що і ваш оригінальний прапорець. І коли я намагався завантажувати масив прапорців за допомогою document.GetElemtentsByName (), ви можете здогадатися, як все зіпсувалося. Це химерно.


4

З того, що я можу зібрати, модель не хоче здогадуватися, чи встановлено прапорець = істинне чи помилкове, я обійшов це, встановивши атрибут значення на елементі галочки за допомогою jQuery, перш ніж надсилати форму такої форми:

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

Таким чином, вам не потрібен прихований елемент просто для зберігання значення прапорця.


4

Я знаю, що це запитання було написане, коли MVC3 не вийшов, але для всіх, хто приходить до цього питання та використовує MVC3, ви можете захотіти "правильного" способу цього зробити.

Поки я думаю, що роблю все

Contains("true");

річ чудова і чиста, і вона працює у всіх версіях MVC, проблема полягає в тому, що вона не враховує культуру (наче вона справді має значення у випадку булінгу).

"Правильний" спосіб з'ясувати значення 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

Ні з якої причини, я не бачу жодної користі для одного способу над іншим.
Dan VanWinkle

1
Вибачте, насправді є причина. Я просто подивився на мій код, і я використовую цей один метод для 5 різних дзвінків, і мені не здалося, що я передаю FormCollection від кожного методу. Це було найпростішим способом дістати ключі, не потрібно нічого пропускати навколо.
Dan VanWinkle

так, я новачок у 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);
        }

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>
<% } %>

Дякуємо за відповідь, але це питання ювіст трохи старе. Шість років, о. Ви можете відредагувати та вказати, яку версію MVC ви використовуєте. Це допоможе кожному, хто використовує новішу версію, знайти цю відповідь.

Я другий, що. Видалення унікального посвідчення вирішило це питання після багатьох головних болів та скрегіт зубів
Оді

Для цього питання використовується версія MVC, яку я використовую .net MVC 4.0.
ChinaHelloWorld

2

це те, що я зробив, щоб втратити подвійні значення при використанні Html.CheckBox (...

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

з чотирма прапорцями, невірно встановленими, неперевіреними, перевіреними, це виявляється правдивим, хибним, хибним, помилковим, правдивим, хибним, істинним, хибним, помилковим, правдивим. саме те, що мені було потрібно


0

Як щодо чогось подібного?

bool isChecked = false;
if (Boolean.TryParse(Request.Form.GetValues(”chkHuman”)[0], out isChecked) == false)
    ModelState.AddModelError(”chkHuman”, Nice try.”);

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оскільки це повертається як масив.


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);

    }
});

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- дійсно-булева /


Вам не потрібно використовувати для цього Javascript ... var isChecked = formData [ключ] .Contains ("true");
Джаммер

0

У мене була майже та сама проблема, але повернене значення мого контролера було заблоковано іншими значеннями.

Знайдено просте рішення, але воно здається трохи грубим.

Спробуйте ввести Viewbag.свій контролер, і тепер ви даєте йому таке ім’яViewbag.Checkbool

Тепер перейдіть до перегляду і спробуйте це @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()" />

0

Використовуючи @mmacaulay, я придумав це для bool:

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

Якщо встановлено прапорець активний = вірно

Якщо відмічено активний = хибний

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.