Чому CheckBoxFor надає додатковий тег введення, і як я можу отримати значення за допомогою FormCollection?


130

У моєму додатку ASP.NET MVC я відображаю прапорець із використанням наступного коду:

<%= Html.CheckBoxFor(i=>i.ReceiveRSVPNotifications) %>

Тепер я бачу, що це відображає і тег введення прапорця, і тег прихованого введення. Проблема, яка виникає у мене, полягає в тому, що я намагаюся отримати значення з прапорця за допомогою FormCollection:

FormValues["ReceiveRSVPNotifications"]

Я отримую значення "вірно, помилково". Переглядаючи виведений HTML, я бачу таке:

 <input id="ReceiveRSVPNotifications" name="ReceiveRSVPNotifications" value="true" type="checkbox">
 <input name="ReceiveRSVPNotifications" value="false" type="hidden">

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

Будь-які ідеї?

Відповіді:


168

Подивіться тут:

http://forums.asp.net/t/1314753.aspx

Це не помилка, а насправді той самий підхід, який використовують і Ruby on Rails, і MonoRail.

Коли ви надсилаєте форму з прапором, значення розміщується лише у тому випадку, якщо прапорець встановлений. Отже, якщо ви не залишите прапорець без позначення, то на сервер нічого не буде надіслано, коли в багатьох ситуаціях ви хочете, щоб надіслати помилкове. Оскільки прихований вхід має те саме ім'я, що і прапорець, тоді, якщо прапорець не встановлений, ви все одно отримаєте "false", надісланий на сервер.

Коли прапорець встановлений, ModelBinder автоматично подбає про вилучення "true" з "true, false"


10
Це хороше пояснення, але в деяких випадках ви можете отримати "нічого" в рядку запиту, коли ckecbox не перевірено - значить, поведінка за замовчуванням. Чи можливо це за допомогою помічника Html або мені просто потрібно використовувати <input>тег.
Максиміліан Маєр

13
Мені це не подобається .... У мене є сторінка пошуку, яка використовує GET, і тепер моя URL-адреса сповнена правдивих / помилкових парам ...
Shawn Mclean

1
Нічого, це несподівано. Чи означає це, що прапорець HTML насправді "зламаний"?
Ісантіпов

1
@Isantipov: Ні, це просто означає, що ці веб-рамки не покладаються на відсутність розміщеного значення для позначення прапорця false. Подивіться на відповідь RyanJMcGowan нижче: "
Роберт Харві

1
Ну @ Роберт, це не працює для мене. Якщо мій chbox MyCheckbox не встановлений, я отримую помилкове значення, коли MyCheckbox перевіряється, я отримую масив [true, false] як його значення. Я серіалізую всю форму і передаю її через ajax до дії контролера, наприклад. AddToOrder (модель MyModel), де я отримую модель.MyCheckbox = false замість true
nickornotto

18

У мене була така ж проблема, як у Шона (вище). Цей підхід може бути чудовим для POST, але він дійсно корисний для GET. Тому я реалізував просте розширення Html, яке просто вибиває приховане поле.

public static MvcHtmlString BasicCheckBoxFor<T>(this HtmlHelper<T> html, 
                                                Expression<Func<T, bool>> expression,
                                                object htmlAttributes = null)
{
    var result = html.CheckBoxFor(expression).ToString();
    const string pattern = @"<input name=""[^""]+"" type=""hidden"" value=""false"" />";
    var single = Regex.Replace(result, pattern, "");
    return MvcHtmlString.Create(single);
}

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


5
Ви не відчуваєте себе трохи брудною, що використовували регулярний вирівнювання, щоб відповідати / видаляти прихований вхід, а не просто створювати лише вхідний прапорець? :)
Тім Хоббс

2
Ні, я зробив це так, що я вносив зміни до існуючої поведінки, а не повторно реалізовував своє. Таким чином, моя зміна буде йти в ногу з будь-якими змінами, що з'явилися у МС.
Кріс Кемп

10
Просто невелике спостереження. Ваша реалізація не відображатиме жодних спеціальних атрибутів, які передаються. Параметр "htmlAttributes" не передається оригінальному методу "CheckBoxFor".
Еміль Лундін

16

Я використовую цей альтернативний метод для відображення прапорців для GET форм:

/// <summary>
/// Renders checkbox as one input (normal Html.CheckBoxFor renders two inputs: checkbox and hidden)
/// </summary>
public static MvcHtmlString BasicCheckBoxFor<T>(this HtmlHelper<T> html, Expression<Func<T, bool>> expression, object htmlAttributes = null)
{
    var tag = new TagBuilder("input");

    tag.Attributes["type"] = "checkbox";
    tag.Attributes["id"] = html.IdFor(expression).ToString();
    tag.Attributes["name"] = html.NameFor(expression).ToString();
    tag.Attributes["value"] = "true";

    // set the "checked" attribute if true
    ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
    if (metadata.Model != null)
    {
        bool modelChecked;
        if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked))
        {
            if (modelChecked)
            {
                tag.Attributes["checked"] = "checked";
            }
        }
    }

    // merge custom attributes
    tag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));

    var tagString = tag.ToString(TagRenderMode.SelfClosing);
    return MvcHtmlString.Create(tagString);
}

Це схоже на метод Кріса Кемпа , який працює чудово, за винятком того, що цей метод не використовує основні CheckBoxForта Regex.Replace. Він заснований на джерелі оригінального Html.CheckBoxForметоду.


Це абсолютно найкраще рішення цієї проблеми і повинно бути частиною рамки MVC ... вона працює чудово. Я не впевнений, чому ваша відповідь не привернула більше уваги ... дякую!
користувач2315985

@ user2315985: Я розмістив це трохи пізно;) це причина.
Том Пажурек

Як ви обробляєте властивості колекції? Зв’язування моделі за замовчуванням припиняє значення прив'язки, коли вона стикається з розривом у зв'язаних індексах, тому здається, що ви використовуєте розширення, якщо, скажімо, було перевірено перше і останнє значення, ви ніколи не дізнаєтесь про друге значення, яке ви повинні отримувати . Або я щось пропускаю?
Tieson T.

@TiesonT. Якщо ви не хочете написати власну палітурку моделі, єдиним рішенням є запобігання прогалин. Цей метод не надсилає помилки для невірно встановлених прапорців, тому, можливо, ви хочете використовувати оригінальний метод Html.CheckBoxFor, який це робить.
Том Пажурек

10

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

if (inputType == InputType.CheckBox)
{
    // Render an additional <input type="hidden".../> for checkboxes. This
    // addresses scenarios where unchecked checkboxes are not sent in the request.
    // Sending a hidden input makes it possible to know that the checkbox was present
    // on the page when the request was submitted.
    StringBuilder inputItemBuilder = new StringBuilder();
    inputItemBuilder.Append(tagBuilder.ToString(TagRenderMode.SelfClosing));

    TagBuilder hiddenInput = new TagBuilder("input");
    hiddenInput.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
    hiddenInput.MergeAttribute("name", fullName);
    hiddenInput.MergeAttribute("value", "false");
    inputItemBuilder.Append(hiddenInput.ToString(TagRenderMode.SelfClosing));
    return MvcHtmlString.Create(inputItemBuilder.ToString());
}

9

Я думаю, що найпростішим рішенням є надання елемента INPUT безпосередньо таким чином:

<input type="checkbox" 
       id="<%=Html.IdFor(i => i.ReceiveRSVPNotifications)%>"
       name="<%=Html.NameFor(i => i.ReceiveRSVPNotifications)%>"
       value="true"
       checked="<%=Model.ReceiveRSVPNotifications ? "checked" : String.Empty %>" />

У синтаксисі Razor це ще простіше, тому що атрибут "перевірений" безпосередньо виводиться із "перевіреним" значенням, коли йому надається значення "справжнє" на стороні сервера.

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