CheckBoxForが追加の入力タグをレンダリングするのはなぜですか?また、FormCollectionを使用して値を取得するにはどうすればよいですか?


130

私のASP.NET MVCアプリでは、次のコードを使用してチェックボックスをレンダリングしています。

<%= Html.CheckBoxFor(i=>i.ReceiveRSVPNotifications) %>

これで、チェックボックスの入力タグと非表示の入力タグの両方がレンダリングされることがわかります。私が抱えている問題は、FormCollectionを使用してチェックボックスから値を取得しようとするときです。

FormValues["ReceiveRSVPNotifications"]

「true、false」という値を取得します。レンダリングされたHTMLを見ると、次のことがわかります。

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

したがって、FormValuesコレクションは同じ名前を持つため、これら2つの値を結合するように見えます。

何か案は?

回答:


168

ここを見てください:

http://forums.asp.net/t/1314753.aspx

これはバグではなく、実際にはRuby on RailsとMonoRailの両方が使用するアプローチと同じです。

チェックボックス付きのフォームを送信すると、チェックボックスがオンの場合にのみ値が送信されます。そのため、チェックボックスをオフのままにすると、多くの状況で代わりにfalseを送信したい場合、何もサーバーに送信されません。非表示の入力はチェックボックスと同じ名前であるため、チェックボックスがオフの場合でも、サーバーに「false」が送信されます。

チェックボックスをオンにすると、ModelBinderは「true、false」から「true」を自動的に抽出します


10
これは良い説明ですが、一部のケースでは、チェックボックスがチェックされていないときにクエリ文字列で「何も」を取得したい場合があるので、デフォルトの動作です。これはHTMLヘルパーを使用して可能ですか、または単に<input>タグを使用する必要がありますか?
Maksymilian Majer

13
私はこれが好きではありません。...GETを使用する検索ページがあり、私のURLはtrue / falseパラメータでいっぱいです...
Shawn Mclean

1
うわー、それは予想外です。これは、HTMLチェックボックスが実際に「壊れている」という意味ですか?
Isantipov 2014年

1
@Isantipov:いいえ、それは、これらのWebフレームワークが、チェックボックスにポストされた値が存在しないことに依存していないことを意味するだけfalseです。以下のRyanJMcGowanの回答を見てください。「非表示の入力を送信すると、リクエストが送信されたときにページにチェックボックスが存在していたことがわかります。」
Robert Harvey 14年

1
まあ@ロバート、これは私にはうまくいきません。私のchbox MyCheckboxがチェックされていない場合は、falseが返されます。MyCheckboxがチェックされていると、値として[true、false]の配列が返されます。私はフォーム全体をシリアル化し、それをajaxを介してコントローラアクションに渡します。AddToOrder(MyModel model)where where get.MyCheckbox = false
where

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
あなたは隠された入力を削除/一致するだけでなく構築するために正規表現を使用することをビット汚い感じていないだけで、チェックボックスの入力を?:)
Tim Hobbs 2013年

2
いいえ、自分自身を再実装するのではなく、既存の動作を修正するためにそれを行いました。そうすれば、私の変更はMSが展開した変更に対応できます。
Chris Kemp 2013

10
ほんの小さな観察。実装では、渡されたカスタム属性はレンダリングされません。パラメータ「htmlAttributes」は元の「CheckBoxFor」メソッドに渡されません。
Emil Lundin、2013年

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

これは、基になるandを使用しないことを除いて、正常に機能しているChris Kempのメソッドに似ています。元のメソッドのソースに基づいています。CheckBoxForRegex.ReplaceHtml.CheckBoxFor


これはこの問題に対する絶対的な最善の解決策であり、MVCフレームワークの一部である必要があります...完全に機能します。なぜあなたの答えがもっと注目されなかったのか分かりません...ありがとうございます!
user2315985

@ user2315985:少し遅すぎて投稿しました;)それが理由です。
トムパウレク

コレクションのプロパティをどのように処理していますか?デフォルトのモデルバインダーは、バインドされたインデックスにギャップが発生すると値のバインドを停止するため、拡張機能を使用しているように見えます。 。または、何か不足していますか?
Tieson T.

@TiesonT。独自のモデルバインダーを作成する場合を除き、唯一の解決策はギャップを防ぐことです。このメソッドは、チェックされていないチェックボックスに対してfalseを送信しないので、それを行う元のHtml.CheckBoxForメソッドを使用することができます。
TomPažourek16年

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

Razor構文では、 'checked'属性は、 'true'のサーバー側の値が指定されたときに「checked」値で直接レンダリングされるため、さらに簡単です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.