asp.net mvc: ทำไม Html.CheckBox จึงสร้างอินพุตที่ซ่อนเพิ่มเติม


215

ฉันเพิ่งสังเกตเห็นว่าHtml.CheckBox("foo")สร้าง 2 อินพุตแทนที่จะเป็นหนึ่งใครรู้ว่าทำไมถึงเป็นเช่นนั้น?

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


1
คำตอบของ ericvgในคำถามที่ซ้ำกันที่เป็นไปได้ยังอธิบายสิ่งที่เครื่องผูกโมเดลทำเมื่อส่งทั้งช่องทำเครื่องหมายและฟิลด์ที่ซ่อนอยู่
Theophilus

1
ฉันเกลียดสิ่งนี้มันคึกคักขึ้นกับ jQuery ของฉัน
Jerry Liang

2
การใช้งานแปลก ๆ โดย mvc การส่งค่าทั้งสองไม่สมเหตุสมผลเลย ฉันตรวจสอบคำขอจาก ["myCheckBox"] และค่าของมันเป็นจริงเท็จ WTF ฉันต้องเขียนตัวควบคุมด้วยตนเองในมุมมอง
Nick Masao

หากสิ่งนี้เป็นสิ่งที่ไม่พึงประสงค์จริงๆอย่าใช้ Html.CheckBox (... ) และเพียงป้อน html สำหรับช่องทำเครื่องหมาย
wnbates

คำตอบ:


198

หากไม่ได้เลือกช่องทำเครื่องหมายจะไม่มีการส่งฟิลด์แบบฟอร์ม นั่นคือเหตุผลที่มีค่าเท็จเสมอในฟิลด์ที่ซ่อนอยู่ หากคุณไม่เลือกช่องทำเครื่องหมายฟอร์มจะยังคงมีค่าจากฟิลด์ที่ซ่อนอยู่ นั่นคือวิธีที่ ASP.NET MVC จัดการกับค่าช่องทำเครื่องหมาย

หากคุณต้องการที่จะยืนยันว่าวางช่องทำเครื่องหมายลงในแบบฟอร์มไม่ได้อยู่กับ Html.Hidden <input type="checkbox" name="MyTestCheckboxValue"></input>แต่มี ไม่ต้องทำเครื่องหมายที่ช่องทำเครื่องหมายส่งแบบฟอร์มและดูค่าคำขอที่โพสต์ทางฝั่งเซิร์ฟเวอร์ คุณจะเห็นว่าไม่มีค่าช่องทำเครื่องหมาย หากคุณมีเขตข้อมูลที่ซ่อนอยู่ก็จะมีMyTestCheckboxValueรายการที่มีfalseค่า


52
หากคุณไม่ได้ทำเครื่องหมายในช่องแล้วเห็นได้ชัดว่าคำตอบนั้นเป็นเท็จไม่จำเป็นต้องให้มันส่งคืนรายการ การออกแบบที่โง่ในความคิดของฉัน
มัฟฟินแมน

54
@TheMuffinMan: นี่ไม่ใช่ตัวเลือกที่โง่ ให้บอกว่าแบบจำลองการดูของคุณมีคุณสมบัติที่เรียกว่าIsActiveซึ่งเริ่มต้นtrueใน constructor ผู้ใช้ไม่ได้เลือกช่องทำเครื่องหมาย แต่เนื่องจากค่าไม่ถูกส่งไปยังเซิร์ฟเวอร์ตัวยึดโมเดลจะไม่มารับและค่าคุณสมบัติจะไม่เปลี่ยนแปลง Model binder ไม่ควรสันนิษฐานว่าหากไม่มีการส่งค่ามันจะถูกตั้งค่าเป็นเท็จเพราะอาจเป็นการตัดสินใจของคุณที่จะไม่ส่งค่านี้
LukLed

21
มันเริ่มต้นด้วยช่องทำเครื่องหมายที่ไม่เป็นอันตรายและก่อนที่คุณจะรู้ว่าเรามีสถานะการดูแล้วมันจะเปลี่ยนเป็น ASP.NET MVCForms
มัฟฟินแมน

4
@LukLed กำลังตอบกลับความคิดเห็นของคุณสาย ... ฉันคิดว่าตรรกะนั้นมีข้อบกพร่องเพราะหากโมเดลการดูของคุณมีประเภทคุณสมบัติ int และไม่มีการป้อนข้อมูลในแบบฟอร์มค่าเริ่มต้นคือ 0 เพราะไม่มีค่าที่จะเปลี่ยนแปลง เช่นเดียวกันกับคุณสมบัติอื่น ๆ ค่าเริ่มต้นสำหรับสตริงเป็นโมฆะ หากคุณไม่ได้ส่งค่ามันเป็นโมฆะ ในตัวอย่างของคุณเกี่ยวกับการตั้งค่าคุณสมบัติเป็นจริงในตัวสร้างนั่นคือการตัดสินใจออกแบบที่ไม่ดีและตอนนี้มันเริ่มสว่างเนื่องจากวิธีการทำงานของช่องทำเครื่องหมายในที่ดิน http
มัฟฟินแมน

8
ตรรกะการแชโดว์นี้แบ่งสำหรับช่องทำเครื่องหมายที่ปิดใช้งาน - พวกเขาส่งfalseค่าแม้ว่าจะถูกตรวจสอบและนั่นทำให้เกิดความสับสน ช่องทำเครื่องหมายที่ปิดใช้งานไม่ควรส่งค่าใด ๆ เลยถ้า ASP.NET ต้องการให้เข้ากันได้กับลักษณะการทำงาน HTTP เริ่มต้น
JustAMartin

31

คุณสามารถเขียนผู้ช่วยเพื่อป้องกันการเพิ่มอินพุตที่ซ่อนอยู่:

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

ใช้มัน:

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

4
สิ่งนี้ทำให้ฉันสะดุดสักนาทีดังนั้นในกรณี ... หากผู้ช่วยของคุณอยู่ในเนมสเปซอย่าลืมเพิ่ม@using Your.Name.Spaceที่ด้านบนของไฟล์มุมมอง. cshtml ของคุณ
ooXei1sh

3
@ ooXei1sh อย่างใดอย่างหนึ่งหรือทำให้ผู้ช่วยของคุณใน namespace System.Web.Mvc.Htmlให้สามารถเข้าถึงได้ในทุกมุมมอง
ลุค

1
หากคุณต้องการใช้สิ่งนี้สำหรับการโพสต์กลับหรือสำหรับการโพสต์ผ่าน ajax ด้วยค่าแบบฟอร์มคุณจะต้องจัดการการตั้งค่าเป็นจริงหรือเท็จผ่านเหตุการณ์ onchange บนช่องทำเครื่องหมาย @ Html.CheckBoxSimple (x => Model.Foo ใหม่ {value = Model.Foo, onchange = "toggleCheck (this)"}) จากนั้นในฟังก์ชั่นจาวาสคริปต์ ToggleCompleted (el) {var checked = $ (el) .is (': checked'); $ ( '# ฟู') Val (ตรวจสอบ). }
John81


8

คู่มือวิธีการคือ:

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

1
และสิ่งนี้รับประกันว่าจะให้คุณได้รับค่าชนิดช่องทำเครื่องหมายและไม่ใช่ค่าประเภทที่ซ่อนอยู่?
Brian Sweeney

1
ที่ไม่เกี่ยวข้อง เมื่อไม่ได้ทำเครื่องหมายในช่องทำเครื่องหมายจะไม่โพสต์ ดังนั้นกล่องซ่อนจะมี 'เท็จ' หากช่องทำเครื่องหมายถูกตรวจสอบค่าของ 'จริงจริง' จะถูกส่งกลับ (ฉันคิดว่า) และสิ่งนี้ไม่เท่ากับ 'เท็จ'
Valamas

16
ไม่เมื่อทำเครื่องหมายที่ช่องทำเครื่องหมายทั้งจริงและเท็จจะถูกโพสต์เพราะทั้งช่องทำเครื่องหมายและเขตข้อมูลที่ซ่อนอยู่เป็นตัวควบคุมที่ถูกต้องที่จะส่งกลับ Mvc binder จะตรวจสอบว่ามีค่าจริงสำหรับ namve ที่ระบุหรือไม่และถ้าต้องการค่าจริง คุณสามารถตรวจสอบสิ่งนี้ได้โดยดูข้อมูลที่โพสต์ มันจะมีทั้งค่าจริงและเท็จกับชื่อเดียว
ZafarYousafi

สิ่งนี้ทำให้ฉันครั้งเดียวฉันกำลังตรวจสอบว่าค่า == จริง
เทอร์รี่ Kernan

8

นี่เป็นวิธีแก้ปัญหาของ 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

ใช้มี, มันจะทำงานกับค่าโพสต์ที่เป็นไปได้สองค่า: "false" หรือ "true, false"

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

1

อินพุตที่ซ่อนอยู่ก่อให้เกิดปัญหากับช่องทำเครื่องหมายสไตล์ ดังนั้นฉันจึงสร้างส่วนขยาย Html Helper เพื่อวางอินพุตที่ซ่อนอยู่นอก div ที่มีช่องทำเครื่องหมาย

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

ผลลัพธ์

<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

นี่ไม่ใช่ข้อผิดพลาด! มันเพิ่มความเป็นไปได้ของการมีค่าเสมอหลังจากโพสต์แบบฟอร์มไปยังเซิร์ฟเวอร์ หากคุณต้องการที่จะจัดการกับช่องใส่ช่องทำเครื่องหมายด้วย jQuery ใช้วิธีการ prop (ผ่านคุณสมบัติ 'ตรวจสอบ' เป็นพารามิเตอร์) ตัวอย่าง:$('#id').prop('checked')


0

คุณสามารถลองเริ่มตัวสร้างโมเดลของคุณแบบนั้นได้:

public MemberFormModel() {
    foo = true;
}

และในมุมมองของคุณ:

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

0

ฉันพบสิ่งนี้ทำให้เกิดปัญหาจริง ๆ เมื่อฉันมี WebGrid ลิงก์การเรียงลำดับใน WebGrid จะเปิดขึ้นโดยการสืบค้นสองเท่าหรือ x = true & x = false เป็น x = true, false และทำให้เกิดข้อผิดพลาดในการแยกวิเคราะห์ในช่องทำเครื่องหมาย

ฉันลงเอยด้วยการใช้ jQuery เพื่อลบฟิลด์ที่ซ่อนอยู่ในฝั่งไคลเอ็นต์:

    <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>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.