asp.net mvc: perché Html.CheckBox sta generando un ulteriore input nascosto


215

Ho appena notato che Html.CheckBox("foo")genera 2 input invece di uno, qualcuno sa perché è così?

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

3
possibile duplicato del metodo HTML.Checkbox
Matt,

1
La risposta di ericvg nella possibile domanda duplicata spiega anche cosa fa il raccoglitore di modelli quando vengono inviati sia la casella di controllo che i campi nascosti.
Teofilo,

1
Odio questa cosa che si stava accumulando con il mio jquery.
Jerry Liang,

2
Strana implementazione di mvc. L'invio di entrambi i valori non ha alcun senso. Ho controllato Request.From ["myCheckBox"] e il suo valore era vero, falso. WTF. Ho dovuto scrivere manualmente il controllo nella vista.
Nick Masao,

Se questo è davvero indesiderato, allora non usare Html.CheckBox (...) e inserisci semplicemente l'html per una casella di controllo
wnbates,

Risposte:


198

Se la casella di controllo non è selezionata, il campo modulo non viene inviato. Ecco perché c'è sempre un valore falso nel campo nascosto. Se si lascia deselezionata la casella di controllo, il modulo avrà comunque valore dal campo nascosto. Ecco come ASP.NET MVC gestisce i valori della casella di controllo.

Se vuoi confermarlo, posiziona una casella nel modulo non con Html. Nascosto, ma con <input type="checkbox" name="MyTestCheckboxValue"></input>. Lascia la casella deselezionata, invia il modulo e controlla i valori delle richieste pubblicate sul lato server. Vedrai che non esiste un valore per la casella di controllo. Se avessi un campo nascosto, conterrebbe la MyTestCheckboxValuevoce con falsevalore.


52
Se non hai selezionato la casella, ovviamente la risposta è falsa, non è necessario che rispedisca l'articolo. Design sciocco secondo me.
The Muffin Man,

54
@TheMuffinMan: questa non è un'opzione sciocca. Supponiamo che il tuo modello di vista abbia una proprietà chiamata IsActive, che viene avviata truenel costruttore. L'utente deseleziona la casella di controllo, ma poiché il valore non viene inviato al server, il raccoglitore modello non lo raccoglie e il valore della proprietà non viene modificato. Il raccoglitore modello non dovrebbe presumere che, se il valore non viene inviato, è stato impostato su false, poiché potrebbe essere la decisione di non inviare questo valore.
LukLed

21
Si inizia con una casella di controllo innocua e poi prima che tu lo sappia abbiamo lo stato di visualizzazione, quindi si evolve in ASP.NET MVCForms.
The Muffin Man,

4
@LukLed In risposta al tuo commento in ritardo ... Penso che la logica sia errata perché se il tuo modello di vista ha un tipo di proprietà int e non c'è input nel modulo, il valore predefinito è 0 perché non è stato trovato alcun valore per cambiarlo. Lo stesso vale con qualsiasi altra proprietà, il valore predefinito per una stringa è null. Se non si invia un valore, è nullo. Nel tuo esempio di impostazione di una proprietà su true nel costruttore, questa è onestamente una cattiva decisione di progettazione e ora viene alla luce a causa del funzionamento delle caselle di controllo in http land.
The Muffin Man

8
Questa logica di shadowing si interrompe per le caselle di controllo disabilitate: inviano falsevalore anche se sono selezionate e questo è confuso. Le caselle di controllo disabilitate non devono inviare alcun valore se ASP.NET desidera essere compatibile con il comportamento HTTP predefinito.
JustAMartin,

31

Puoi scrivere un aiuto per evitare di aggiungere l'input nascosto:

using System.Web.Mvc;
using System.Web.Mvc.Html;

public static class HelperUI
{
    public static MvcHtmlString CheckBoxSimple(this HtmlHelper htmlHelper, string name, object htmlAttributes)
    {
        string checkBoxWithHidden = htmlHelper.CheckBox(name, htmlAttributes).ToHtmlString().Trim();
        string pureCheckBox = checkBoxWithHidden.Substring(0, checkBoxWithHidden.IndexOf("<input", 1));
        return new MvcHtmlString(pureCheckBox);
    }
}

usalo:

@Html.CheckBoxSimple("foo", new {value = bar.Id})

4
Questo mi ha fatto scattare per un minuto, quindi per ogni evenienza ... Se il tuo aiutante si trova in uno spazio dei nomi, non dimenticare di aggiungere @using Your.Name.Spaceall'inizio del file di visualizzazione del rasoio .cshtml.
ooXei1sh

3
@ ooXei1sh O quello, o metti il ​​tuo aiutante nello spazio dei nomi System.Web.Mvc.Htmlper essere accessibile in tutte le viste
Luca

1
Se si desidera utilizzarlo per un postback del modulo o per la pubblicazione tramite ajax con i valori del modulo, sarà necessario gestire l'impostazione del valore su true o false tramite l'evento onchange nella casella di controllo. @ Html.CheckBoxSimple (x => Model.Foo, new {value = Model.Foo, onchange = "toggleCheck (this)"}). Quindi nella funzione javascript ToggleCompleted (el) {var checked = $ (el) .is (': checked'); $ ( '# Foo') val (selezionata).; }
John81

13

quando la casella di controllo è selezionata e inviata, eseguire ciò

if ($('[name="foo"]:checked').length > 0)
    $('[name="foo"]:hidden').val(true);

Fare riferimento


8

L'approccio manuale è questo:

bool IsDefault = (Request.Form["IsDefault"] != "false");

1
E questo è garantito per ottenere il valore del tipo di casella di controllo e non il valore del tipo nascosto?
Brian Sweeney,

1
questo è irrilevante. Quando la casella di controllo non è selezionata, non viene pubblicata. Quindi la scatola nascosta avrà 'falso'. Se la casella è selezionata, viene restituito il valore "true, true" (penso) e questo non equivale a "false".
Valamas,

16
No, quando la casella di controllo è selezionata, sia vero che falso vengono pubblicati perché sia ​​la casella di controllo che i campi nascosti sono controlli validi da inviare. Il raccoglitore Mvc controlla quindi se esiste un valore vero per un determinato nome e, in tal caso, preferisce il valore vero. Puoi verificarlo visualizzando i dati pubblicati. Avrà valori sia veri che falsi rispetto al singolo nome.
Zafar Yousafi,

Questo mi ha fatto una volta, stavo controllando se il valore == true
Terry Kernan il

8

Questa è la versione fortemente tipizzata della soluzione di Alexander Trofimov:

using System.Web.Mvc;
using System.Web.Mvc.Html;

public static class HelperUI
{
    public static MvcHtmlString CheckBoxSimpleFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, object htmlAttributes)
    {
        string checkBoxWithHidden = htmlHelper.CheckBoxFor(expression, htmlAttributes).ToHtmlString().Trim();
        string pureCheckBox = checkBoxWithHidden.Substring(0, checkBoxWithHidden.IndexOf("<input", 1));
        return new MvcHtmlString(pureCheckBox);
    }
}

4

Usa Contiene, funzionerà con i due possibili valori di post: "false" o "true, false".

bool isChecked = Request.Form["foo"].Contains("true");

1

L'input nascosto stava causando problemi con le caselle di controllo con stile. Così ho creato un'estensione Helper HTML per posizionare l'input nascosto all'esterno del div contenente il CheckBox.

using System;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;

namespace YourNameSpace
{
    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString CustomCheckBoxFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string labelText)
        {
            //get the data from the model binding
            var fieldName = ExpressionHelper.GetExpressionText(expression);
            var fullBindingName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(fieldName);
            var fieldId = TagBuilder.CreateSanitizedId(fullBindingName);
            var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            var modelValue = metaData.Model;

            //create the checkbox
            TagBuilder checkbox = new TagBuilder("input");
            checkbox.MergeAttribute("type", "checkbox");
            checkbox.MergeAttribute("value", "true"); //the visible checkbox must always have true
            checkbox.MergeAttribute("name", fullBindingName);
            checkbox.MergeAttribute("id", fieldId);

            //is the checkbox checked
            bool isChecked = false;
            if (modelValue != null)
            {
                bool.TryParse(modelValue.ToString(), out isChecked);
            }
            if (isChecked)
            {
                checkbox.MergeAttribute("checked", "checked");
            }

            //add the validation
            checkbox.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(fieldId, metaData));

            //create the outer div
            var outerDiv = new TagBuilder("div");
            outerDiv.AddCssClass("checkbox-container");

            //create the label in the outer div
            var label = new TagBuilder("label");
            label.MergeAttribute("for", fieldId);
            label.AddCssClass("checkbox");

            //render the control
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(outerDiv.ToString(TagRenderMode.StartTag));
            sb.AppendLine(checkbox.ToString(TagRenderMode.SelfClosing));
            sb.AppendLine(label.ToString(TagRenderMode.StartTag));
            sb.AppendLine(labelText); //the label
            sb.AppendLine("<svg width=\"10\" height=\"10\" class=\"icon-check\"><use xlink:href=\"/icons.svg#check\"></use></svg>"); //optional icon
            sb.AppendLine(label.ToString(TagRenderMode.EndTag));
            sb.AppendLine(outerDiv.ToString(TagRenderMode.EndTag));

            //create the extra hidden input needed by MVC outside the div
            TagBuilder hiddenCheckbox = new TagBuilder("input");
            hiddenCheckbox.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
            hiddenCheckbox.MergeAttribute("name", fullBindingName);
            hiddenCheckbox.MergeAttribute("value", "false");
            sb.Append(hiddenCheckbox.ToString(TagRenderMode.SelfClosing));

            //return the custom checkbox
            return MvcHtmlString.Create(sb.ToString());
        }

Risultato

<div class="checkbox-container">
    <input checked="checked" id="Model_myCheckBox" name="Model.myCheckBox" type="checkbox" value="true">
    <label class="checkbox" for="Model_myCheckBox">
        The checkbox label
        <svg width="10" height="10" class="icon-check"><use xlink:href="/icons.svg#check"></use></svg>
    </label>
</div>
<input name="Model.myCheckBox" type="hidden" value="false">

0

Questo non è un bug! Aggiunge la possibilità di avere sempre un valore, dopo aver pubblicato il modulo sul server. Se si desidera gestire i campi di input della casella di controllo con jQuery, utilizzare il metodo prop (passare la proprietà 'checked' come parametro). Esempio:$('#id').prop('checked')


0

Puoi provare a inizializzare il costruttore del tuo Modello in questo modo:

public MemberFormModel() {
    foo = true;
}

e a tuo avviso:

@html.Checkbox(...)
@html.Hidden(...)

0

Ho scoperto che questo ha causato problemi quando avevo un WebGrid. I collegamenti di ordinamento su WebGrid si trasformerebbero per il querystring raddoppiato o x = true & x = false in x = true, false e causerebbero un errore di analisi nella casella di controllo per.

Ho finito con jQuery per eliminare i campi nascosti sul lato client:

    <script type="text/javascript">
    $(function () {
        // delete extra hidden fields created by checkboxes as the grid links mess this up by doubling the querystring parameters
        $("input[type='hidden'][name='x']").remove();
    });
    </script>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.