miso_soup3 Blog

主に ASP.NET 関連について書いています。

ちょっと異色のRadioButtonHelper

HtmlHelperを使ってラジオボタンを生成することについて、長々と説明します。
RadioButtonヘルパーを使うのか? SelectListは使えないのか?
近々MVC入門もありますし、Htmlタグからアプローチしてみたいと思います。

A.ラジオボタンとは(Html)

ラジオボタンとは、チェックボックスやテキストボックスと同じ、ユーザからの入力を受け止めるための1つのコントロールです。
複数の少数の選択肢の中から、1つだけ選択して欲しいときに使用します。

一般的なHtmlタグは以下のようになります。

<div>
 <input type="radio" name="Color" value="Red" id="radio-red" checked> 赤
 <input type="radio" name="Color" value="Green" id="radio-green"> 青
 <input type="radio" name="Color" value="Yellow" id="radio-yellow"> 黄色

 <input type="submit" value="送信" />
</div>
name属性

ユーザはname属性の値が同じであるラジオボタンの中から、1つだけ選択することができます。
上の例では、name属性の値がColorである赤・青・黄色の3つのうち、1つだけ選択することができます。

checked属性

checked属性を指定すると、最初から選択された状態になります。

フォーム送信時で送信されるもの

type="submit"のボタンを押したときは、選択されたラジオボタン
name属性とvalue属性の値がペアとなって送信されます。

label

また、赤や青といった表示は、コントロールに対する名前付けなので、Labelタグでマークアップします。
Labelタグでマークアップする方法は2つあります。

label 1. タグで囲む

1つは、labelタグで、表記とinput要素を囲む方法です。

<label>
 <input type="radio" name="Color" value="Yellow" id="yellow">
 黄色
</label>
label 2. for属性で指定

もう1つは、label要素のfor属性の値を、input要素のid属性の値を指定する方法です。

<input type="radio" name="Color" value="Yellow" id="radio-yellow">
<label for="radio-yellow">赤</label>

ラベルの中のテキストをクリックすると、そのラベルに対応したコントロールが選択された状態になります。
ラジオボタンに限らずコントロールは、対応するラベルをクリックした時でも反応します。)

ユーザのためにも、labelでのマークアップは必ず行ったほうが良いと思います。

B.MVCでのラジオボタン

MVCに限ったことではないのですが、WEBアプリで重要になるのは、ユーザが何を選択したか?です。
つまり、name属性とvalue属性の値を適切に出力することが最も重要です。
次に重要になるのは、checkedや、label要素です。(本当はどっても大事ですが)

MVCにおいても、上のようなHtmlタグを直書きしても大丈夫なのですが、
MVCの機能であるモデルバインドに対応するためには、HtmlHelperを利用すると楽ちんになります。
また、HtmlHelperを利用しても、大事なname属性とvalue属性の値などは隠蔽されません。

ここではモデルバインドについての説明は置いておいて、HtmlHelperを使ってラジオボタンを生成することについて書きます。

C.RadioButtonヘルパー

MVCに搭載されているラジオボタンのヘルパーは以下の2つです。

  • RadioButton
  • RadioButtonFor
@Html.RadioButton("Color", Color.Green)
@Html.RadioButtonFor(m => m.Color, Color.Red)

引数にはそれぞれ、対象となるプロパティの式(または名前)、valueの値を指定します。
上記ではvalueの値にはenum型Colorが指定されています。

name属性の値は、@Html.RadioButtonでは、自由な文字列を設定できます。
下の@Html.RadioButtonForでは、フォームデータを送信した後に、MVCのモデルバインダが
指定したプロパティに値を入れてくれるよう、name属性の値を調整して出力してくれます。

それぞれの出力されるHtmlタグは以下のようになります。

<input id="Color" name="Color" type="radio" value="Green">
<input id="Color" name="Color" type="radio" value="Red">
idの値が一緒になってしまう

ここで少し問題が…id属性の値が、おなじ"Color"になっています。
MVCのヘルパーは、nameに指定された値からidの値を生成するので、ラジオボタンのヘルパーだとidがかぶってしまいます。
idは一要素の固有名と定義されています。
違う値にしたい場合は、

@Html.RadioButtonFor(m => m.Color, Color.Red, new { @id = "red-radio" })

このようにしてidを指定します。

checkedを指定する。

モデルの値ではなく、View側でラジオボタンの初期値を指定したい場合は、

@Html.RadioButtonFor(m => m.Color, Color.Red, new { @checked = "checked" })
@Html.RadioButton("Color", Color.Green, true)

このように指定します。
(モデルで指定している場合は、モデルの値も適用されるので注意)

Labelを使うときの注意
@Html.LabelFor(m => m.Color, "赤")

labelを利用しようと思って、上のようにlabelのヘルパーを使うのはバグへの第一歩かもしれません。
上のヘルパーで出力されるタグは

<label for="Color">赤</label>

なので、どのラベルをクリックしても、ある1つのラジオボタンしか反応しません。

各RadioButtonに違うid属性を指定し、各labelにもForをそれぞれ指定する・・・と
Labelのヘルパーを使ってもうまくいきますが、そのような方法よりも

<label>
 @Html.RadioButtonFor(m => m.Color, Color.Yellow)
 黄
</label>

と囲ってしまったほうが楽です。

RadioButtonまとめ

例えばenumのColorを使って、ラジオボタンを生成したい時は、
Controllerでは

[HttpGet]
public ActionResult Create()
{
    var model = new UserCreateFormModel();
    model.Color = Color.Green; //初期値を設定

    return View(model);
}

Viewでは

<label>
 @Html.RadioButtonFor(m => m.Color, Color.Red, new { @checked = "checked" })
 赤
</label>
<label>
 @Html.RadioButton("Color", Color.Green, true)
 青
</label>
<label>
 @Html.RadioButtonFor(m => m.Color, Color.Yellow)
 黄色
</label>

以上が1つの例になります。
(ちなみに上の場合、初期状態は、青にチェックされた状態となります。)

D.SelectListを使いたい…

「複数の選択肢の中から、ある1つのものを選択する」という用途を満たすコントロールには、
ラジオボタンの他に、ドロップダウンリストがあります。

MVCでドロップダウンリストのヘルパーを使う際は、SelectList(またはSelectListItem)という
便利なオブジェクトを使うことができます。

@Html.DropDownListFor(m => m.Color, Model.ColorSelectListItems)

Text,Value,Selected・・・まさしくラジオボタンの配列にも使用したいところ…と探していたところ、
Mvc3Futuresの中に、SelectListを使ったラジオボタンヘルパーがあることを教えて頂きました。

Mvc3Futures の RadioButtonListFor

使い方です。まずnugetでインストールします。

install-package mvc3futures

対象のヘルパーメソッドは、名前空間Microsoft.Web.Mvcにあるので、Viewに

@using Microsoft.Web.Mvc;

と記載し、

@Html.RadioButtonList("Color", Model.ColorSelectListItems)

...と書くことで、ラジオボタンのリストを生成することができ・・・ますん。
実は、RadioButtonListメソッドが返しているのは、実はMvcHtmlStringの”配列”なので

@foreach(var radioButton in Html.RadioButtonList("Color", Model.ColorSelectListItems))
{
  @radioButton
}

このようにかく必要があります。
しかもラジオボタンだけなので、Labelや表示テキストは自分で書かないといけません。

ソース内を検索してみたところ、MvcHtmlStringの配列を返すメソッドは、このRadioButtonListだけみたいです。

ラジオボタンはDropdownListとは違い、ラジオボタンと表示テキストを、開発者の自由に配置できるので、
このような柔軟なメソッドが用意されたようです。

E.まとめ

MVCでのラジオボタンタグの生成の仕方は、

  • @Html.RadioButton, @Html.RadioButtonFor を利用する
  • Htmlタグを直接書く
  • Mvc3futuresをインストして@Html.RadioButtonList を利用する
  • 自分で拡張メソッドを利用する…※1

少なくともこの4つがあります。(恐らく上になるほど実装が簡単なものです。)

でも、ラジオボタンってそんなに利用されないですよね・・・。

※1 追記

上の問題を解決した RadioButton のヘルパーをしばやんさんが作成されています!
ASP.NET MVC でラジオボタン周りを良い感じに扱う