Чому 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.