ทำไม 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 ใช้งาน

เมื่อคุณส่งแบบฟอร์มที่มีช่องทำเครื่องหมายค่าจะโพสต์เฉพาะในกรณีที่มีการทำเครื่องหมายที่ช่องทำเครื่องหมาย ดังนั้นหากคุณไม่ทำเครื่องหมายในช่องทำเครื่องหมายจะไม่มีสิ่งใดถูกส่งไปยังเซิร์ฟเวอร์เมื่ออยู่ในสถานการณ์ต่าง ๆ ที่คุณต้องการให้ส่งเท็จแทน เนื่องจากอินพุตที่ซ่อนอยู่มีชื่อเดียวกับช่องทำเครื่องหมายดังนั้นหากไม่ได้ทำเครื่องหมายที่ช่องทำเครื่องหมายคุณจะยังคงได้รับ 'เท็จ' ที่ส่งไปยังเซิร์ฟเวอร์

เมื่อทำเครื่องหมายที่ช่องทำเครื่องหมาย ModelBinder จะทำการแยก 'จริง' ออกจาก 'จริงเท็จ' โดยอัตโนมัติ


10
เป็นคำอธิบายที่ดี แต่ในบางกรณีคุณอาจต้องการ 'ไม่มีอะไร' ในสตริงการสืบค้นเมื่อไม่ได้ตรวจสอบ ckecbox ดังนั้นพฤติกรรมเริ่มต้น เป็นไปได้โดยใช้ตัวช่วย Html หรือฉันต้องใช้<input>แท็ก
Maksymilian Majer

13
ฉันไม่ชอบสิ่งนี้ .... ฉันมีหน้าค้นหาที่ใช้ GET และตอนนี้ URL ของฉันเต็มไปด้วย params จริง / เท็จ ...
Shawn Mclean

1
ว้าวนั่นเป็นสิ่งที่ไม่คาดคิด นี่หมายความว่าช่องทำเครื่องหมาย HTML "เสีย" จริงหรือไม่
Isantipov

1
@Isantipov: ไม่มีมันก็หมายความว่าสิ่งเหล่านี้กรอบเว็บไม่พึ่งพาการขาดfalseของค่าโพสต์สำหรับช่องทำเครื่องหมายเพื่อบ่งชี้ ดูคำตอบของ RyanJMcGowan ด้านล่าง: "การส่งอินพุตที่ซ่อนอยู่ทำให้ทราบได้ว่ามีช่องทำเครื่องหมายปรากฏบนหน้าเว็บเมื่อมีการส่งคำขอ"
Robert Harvey

1
ดี @ Robert มันไม่ได้ผลสำหรับฉัน หากไม่ได้ทำการตรวจสอบchbox MyCheckbox ของฉันฉันจะได้รับ false เมื่อMyCheckboxถูกตรวจสอบฉันจะได้รับอาร์เรย์ของ [true, false] เป็นค่า ฉันเป็นอนุกรมทั้งฟอร์มและส่งผ่าน ajax ไปยังแอ็คชั่นคอนโทรลเลอร์เช่น AddToOrder (โมเดล MyModel) ที่ฉันรับ model.MyCheckbox = false แทนที่จะเป็นจริง
nickornotto

18

ฉันมีปัญหาเช่นเดียวกับ Shawn (ด้านบน) วิธีนี้อาจเป็นวิธีที่ดีสำหรับ 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
คุณไม่รู้สึกสกปรกนิดหน่อยที่คุณใช้ regex เพื่อจับคู่ / ลบข้อมูลที่ซ่อนอยู่แทนที่จะสร้างเพียงช่องทำเครื่องหมายอินพุตหรือไม่ :)
Tim Hobbs

2
ไม่ฉันทำเพื่อแก้ไขพฤติกรรมที่มีอยู่มากกว่าที่จะนำไปใช้ใหม่ ด้วยวิธีนี้การเปลี่ยนแปลงของฉันจะเป็นไปตามการเปลี่ยนแปลงใด ๆ ที่ MS ออกมา
Chris Kemp

10
เพียงสังเกตเล็ก ๆ การใช้งานของคุณจะไม่ทำให้แอตทริบิวต์ที่กำหนดเองใด ๆ ที่ส่งผ่าน พารามิเตอร์ "htmlAttributes" ไม่ได้ถูกส่งต่อไปยังวิธี "CheckBoxFor" ดั้งเดิม
Emil Lundin

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

มันคล้ายกับคริสเคมพ์วิธี 's ซึ่งทำงานได้ดียกเว้นคนนี้ไม่ได้ใช้พื้นฐานและCheckBoxFor Regex.Replaceมันขึ้นอยู่กับแหล่งที่มาของHtml.CheckBoxForวิธีการดั้งเดิม


นี่เป็นทางออกที่ดีที่สุดสำหรับปัญหานี้และควรเป็นส่วนหนึ่งของกรอบ MVC ... มันทำงานได้อย่างสมบูรณ์แบบ ฉันไม่แน่ใจว่าทำไมคำตอบของคุณไม่ได้รับความสนใจมากขึ้น ... ขอบคุณ!
2315985

@ user2315985: ฉันโพสต์ช้าไปหน่อย;) นั่นเป็นเหตุผล
Tom Pažourek

คุณจัดการคุณสมบัติการรวบรวมอย่างไร เครื่องผูกรุ่นเริ่มต้นจะหยุดการผูกค่าเมื่อพบช่องว่างในดัชนีที่ถูกผูกไว้ดังนั้นจึงดูเหมือนว่าใช้ส่วนขยายของคุณถ้าพูดว่ามีการตรวจสอบค่าแรกและค่าสุดท้ายคุณไม่เคยรู้เกี่ยวกับค่าที่สองที่คุณควรได้รับ . หรือว่าฉันขาดอะไรไป?
Tieson T.

@TiesonT ถ้าคุณไม่ต้องการเขียนแฟ้มแบบจำลองของคุณเองทางออกเดียวคือการป้องกันช่องว่าง วิธีนี้ไม่ได้ส่งเท็จสำหรับช่องทำเครื่องหมายที่ไม่ได้ทำเครื่องหมายดังนั้นคุณอาจต้องการใช้วิธี Html.CheckBoxFor ดั้งเดิมที่ทำเช่นนั้น
Tom Pažourek

10

นี่คือซอร์สโค้ดสำหรับแท็กอินพุตเพิ่มเติม ไมโครซอฟท์ก็ใจดีพอที่จะรวมความคิดเห็นที่กล่าวถึงเรื่องนี้อย่างแม่นยำ

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 %>" />

ในไวยากรณ์ของมีดโกนจะง่ายยิ่งขึ้นเนื่องจากแอตทริบิวต์ 'ที่ตรวจสอบแล้ว' จะแสดงผลโดยตรงด้วยค่า "ที่ตรวจสอบแล้ว" เมื่อกำหนดค่าฝั่งเซิร์ฟเวอร์ 'ที่แท้จริง'

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