asp.net mvc: mengapa Html.CheckBox menghasilkan input tersembunyi tambahan

215

Saya hanya memperhatikan bahwa Html.CheckBox("foo")menghasilkan 2 input, bukan satu, ada yang tahu mengapa demikian?

<input id="foo" name="foo" type="checkbox" value="true" />
<input name="foo" type="hidden" value="false" /> 
Omu
sumber
1
jawaban ericvg dalam pertanyaan duplikat yang mungkin juga menjelaskan apa yang dilakukan pengikat model saat kotak centang dan bidang tersembunyi dikirim.
Theophilus
1
Aku benci benda ini yang berkumpul dengan jquery saya.
Jerry Liang
2
Implementasi aneh oleh mvc. Mengirimkan kedua nilai tidak masuk akal sama sekali. Saya memeriksa Request.From ["myCheckBox"] dan nilainya benar, salah. WTF. Saya harus menulis kontrol secara manual dalam tampilan.
Nick Masao
Jika ini benar-benar tidak diinginkan maka jangan gunakan Html.CheckBox (...) dan cukup masukan html untuk kotak centang
wnbates

Jawaban:

197

Jika kotak centang tidak dipilih, bidang formulir tidak dikirimkan. Itulah sebabnya selalu ada nilai salah di bidang tersembunyi. Jika Anda meninggalkan kotak centang tidak dicentang, formulir akan tetap memiliki nilai dari bidang tersembunyi. Begitulah cara ASP.NET MVC menangani nilai kotak centang.

Jika Anda ingin mengonfirmasi hal itu, letakkan kotak centang pada formulir bukan dengan Html. Tersembunyi, tetapi dengan <input type="checkbox" name="MyTestCheckboxValue"></input>. Biarkan kotak centang tidak dicentang, kirim formulir dan lihat nilai permintaan yang diposting di sisi server. Anda akan melihat bahwa tidak ada nilai kotak centang. Jika Anda memiliki bidang tersembunyi, ini akan berisi MyTestCheckboxValueentri dengan falsenilai.

LukLed
sumber
52
Jika Anda tidak mencentang kotaknya maka jelas jawabannya salah, tidak perlu mengirimkannya kembali. Desain konyol menurut saya.
The Muffin Man
54
@TheMuffinMan: Ini bukan opsi konyol. Katakanlah model tampilan Anda memiliki properti yang dipanggil IsActive, yang diinisiasi truedalam konstruktor. Pengguna membatalkan centang, tetapi karena nilai tidak dikirim ke server, pengikat model tidak mengambilnya, dan nilai properti tidak berubah. Pengikat model tidak boleh berasumsi, bahwa jika nilai tidak dikirim, itu disetel ke false, karena itu bisa jadi keputusan Anda untuk tidak mengirim nilai ini.
LukLed
21
Dimulai dengan kotak centang yang tidak berbahaya dan kemudian sebelum Anda mengetahuinya kami memiliki status tampilan kemudian berevolusi menjadi ASP.NET MVCForms.
The Muffin Man
4
@LukLed Membalas komentar Anda terlambat ... Saya pikir logika itu cacat karena jika model tampilan Anda memiliki tipe properti int dan tidak ada input pada formulir maka nilai default adalah 0 karena tidak ada nilai yang muncul untuk mengubahnya. Sama halnya dengan properti lainnya, nilai default untuk string adalah nol. Jika Anda tidak mengirim nilai, itu nol. Dalam contoh Anda menyetel properti menjadi true di konstruktor, itu sejujurnya adalah keputusan desain yang buruk dan sekarang terungkap karena cara kerja kotak centang di http land.
The Muffin Man
8
Logika bayangan ini rusak untuk kotak centang dinonaktifkan - mereka mengirim falsenilai bahkan jika mereka diperiksa, dan itu membingungkan. Kotak centang yang dinonaktifkan tidak boleh mengirim nilai apa pun, jika ASP.NET ingin kompatibel dengan perilaku HTTP default.
JustAMartin
31

Anda dapat menulis helper untuk mencegah penambahan input tersembunyi:

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

Gunakan:

@Html.CheckBoxSimple("foo", new {value = bar.Id})
Alexander Trofimov
sumber
4
Ini membuat saya tersandung sebentar, jadi untuk berjaga-jaga ... Jika pembantu Anda di namespace jangan lupa untuk menambahkan @using Your.Name.Spacedi bagian atas file .cshtml razor view Anda.
ooXei1sh
3
@ ooXei1sh Entah itu, atau letakkan pembantu Anda di namespace System.Web.Mvc.Htmlagar dapat diakses di semua tampilan
Luke
1
Jika Anda ingin menggunakan ini untuk postback form atau untuk posting via ajax dengan nilai form maka Anda harus menangani pengaturan nilai true atau false melalui event onchange di kotak centang. @ Html.CheckBoxSimple (x => Model.Foo, baru {value = Model.Foo, onchange = "toggleCheck (this)"}). Kemudian dalam fungsi javascript ToggleCompleted (el) {var checked = $ (el) .is (': checked'); $ ('# Foo'). Val (dicentang); }
John81
12

ketika kotak centang dicentang dan dikirimkan lakukan ini

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

Lihat

Desmond
sumber
8

Pendekatan manual adalah ini:

bool IsDefault = (Request.Form["IsDefault"] != "false");
Valamas
sumber
1
Dan apakah ini dijamin untuk memberi Anda nilai jenis kotak centang dan bukan nilai jenis tersembunyi?
Brian Sweeney
1
itu tidak relevan. Ketika kotak centang tidak dicentang, itu tidak memposting. Jadi hiddenbox akan memiliki 'false'. Jika kotak centang dicentang nilai 'benar, benar' dikembalikan (saya pikir), dan ini tidak sama dengan 'salah'.
Valamas
16
Tidak, ketika kotak centang dicentang, benar dan salah dikirim karena kotak centang dan bidang tersembunyi adalah kontrol yang valid untuk dikirim kembali. Bvc pengikat kemudian memeriksa apakah nilai benar ada untuk nama yang diberikan dan jika demikian lebih memilih nilai sebenarnya. Anda dapat memeriksa ini dengan melihat data yang diposting. Ini akan memiliki nilai benar dan salah terhadap nama tunggal.
ZafarYousafi
Ini sekali saya, saya memeriksa apakah nilai == benar
Terry Kernan
8

Ini adalah versi yang sangat diketik dari solusi 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);
    }
}
Ciro Corvino
sumber
4

Gunakan Berisi, ini akan berfungsi dengan dua kemungkinan nilai posting: "false" atau "true, false".

bool isChecked = Request.Form["foo"].Contains("true");
Dave D
sumber
1

Input tersembunyi menyebabkan masalah dengan kotak centang gaya. Jadi saya membuat Ekstensi Helper Html untuk menempatkan input tersembunyi di luar div yang berisi Kotak Centang.

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

Hasil

<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">
VDWWD
sumber
0

Ini bukan bug! Ini menambahkan kemungkinan selalu memiliki nilai, setelah memposting formulir ke server. Jika Anda ingin menangani bidang input kotak centang dengan jQuery, gunakan metode prop (lulus properti 'diperiksa' sebagai parameter). Contoh:$('#id').prop('checked')

BlueKore
sumber
0

Anda dapat mencoba menginisialisasi konstruktor Model Anda seperti itu:

public MemberFormModel() {
    foo = true;
}

dan menurut Anda:

@html.Checkbox(...)
@html.Hidden(...)
Saïd Mezhoud
sumber
0

Saya menemukan ini benar-benar menyebabkan masalah ketika saya memiliki WebGrid. Tautan pengurutan pada WebGrid akan berubah oleh querystring yang digandakan atau x = true & x = false ke x = true, false dan menyebabkan kesalahan parse pada kotak centang untuk.

Saya akhirnya menggunakan jQuery untuk menghapus bidang tersembunyi di sisi klien:

    <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>
Matthew Lock
sumber