【Rails】ラジオボタンのヘルパーradio_button、collection_radio_buttons、radio_button_tag

バックエンド

Railsでラジオボタンを作るとき、似たようなヘルパーがたくさんあってどれが最適かいつもわからなくなってしまうため、それぞれの特徴と用途をまとめます。

ラジオボタンのヘルパーは3つ

  • radio_button
  • collection_radio_buttons
  • radio_button_tag

モデルに紐づくラジオボタンのときはradio_button、複数のラジオボタンを一括で作成できるのがcollection_radio_buttons。モデルに紐付かない汎用的なラジオボタンのときはradio_button_tag。

サンプルコードの説明

Ruby 3.0.3、Rails 7.0.3を使用します。

ユーザーの登録フォームをscaffoldで作って少し整形しました。これから好きな食べ物をラジオボタンで選択できるようにします。


UserモデルとFoodモデルはfavorite_food_idカラムを外部キーとして一対多の関係です。

class User < ApplicationRecord
	belongs_to :favorite_food, class_name: 'Food'
end
class Food < ApplicationRecord
end

radio_buttonヘルパー

radio_buttonヘルパーを利用して実装する例です。
⬇まずは最小のコードで書いてみます。

<%= form_with(model: user) do |form| %>

	<%= form.radio_button(:favorite_food_id, 1) %>

<% end %>

このようにラジオボタンがひとつだけ作成されました。


HTMLは以下のように出力されます。

<input type="radio" value="1" name="user[favorite_food_id]" id="user_favorite_food_id_1">

⬇すべての食べ物を表示して食べ物の名称も出力するようにしました。

<%= form_with(model: user) do |form| %>
	
	<div class="form_item">
		<%= form.label :favorite_food_id, class: 'form_item_label' %>
		<%- Food.all.each do |food| %>
			<label><%= form.radio_button(:favorite_food_id, food.id) %><%= food.name %></label>
		<%- end %>
	</div>
	
<% end %>


このようにまともな状態になりました。出力されたHTMLは以下の通りです。

<div class="form_item">
	<label class="form_item_label" for="user_favorite_food_id">Favorite food</label>
	<label><input type="radio" value="1" name="user[favorite_food_id]" id="user_favorite_food_id_1">ぎょうざ</label>
	<label><input type="radio" value="2" name="user[favorite_food_id]" id="user_favorite_food_id_2">たまごかけごはん</label>
	<label><input type="radio" value="3" name="user[favorite_food_id]" id="user_favorite_food_id_3">ラーメン</label>
	<label><input type="radio" value="4" name="user[favorite_food_id]" id="user_favorite_food_id_4">たらこスパゲティ</label>
</div>

ラジオボタンは普通、選択肢が複数あると思いますが、radio_buttonヘルパーを使う場合は選択肢はループするなどして自力で実装する必要があります。

ちなみに、checked属性を実装しなくても、編集画面を開くとDBに保存されている状態を再現してくれます。

ラジオボタンのデフォルト値を設定する場合

デフォルトで「ぎょうざ」を選択することにします。viewでぐちゃぐちゃやるより、以下のようにcontrollerで設定するのがシンプルでおすすめです。

# GET /users/new
def new
	@user = User.new
	@user.favorite_food_id = 1
end

collection_radio_buttonsヘルパー

radio_buttonヘルパーは選択肢をループするなどして自力で実装する必要がありましたが、collection_radio_buttonsは1行書くだけで複数のラジオボタンを生成してくれます。

<%= form.collection_radio_buttons(:favorite_food_id, Food.all, :id, :name) %>

⬇上記コードによって出力されたHTMLです。

<input type="hidden" name="user[favorite_food_id]" value="" autocomplete="off">
<input type="radio" value="1" name="user[favorite_food_id]" id="user_favorite_food_id_1">
<label for="user_favorite_food_id_1">ぎょうざ</label>
<input type="radio" value="2" name="user[favorite_food_id]" id="user_favorite_food_id_2">
<label for="user_favorite_food_id_2">たまごかけごはん</label>
<input type="radio" value="3" name="user[favorite_food_id]" id="user_favorite_food_id_3">
<label for="user_favorite_food_id_3">ラーメン</label>
<input type="radio" value="4" name="user[favorite_food_id]" id="user_favorite_food_id_4">
<label for="user_favorite_food_id_4">たらこスパゲティ</label>

一番上に出力されたhiddenは何なんでしょう…?ラジオボタンが何も選択されなかった場合でも空の値を送信するためのものでしょうか。(値を送信しないとDBに空更新がされないため…?ラジオボタンなら入力必須が良いと思うため未検証です。)

ラジオボタンやラベルにclass属性をつけたい、順序を変えたい

タグの順序を変えたり装飾をするためにclass属性を設定することもできます。

⬇collection_radio_buttonsにブロックを与えることで、ラベルとラジオボタンを別々に扱うことができます。

<%= form.collection_radio_buttons(:favorite_food_id, Food.all, :id, :name) do |b| %>
	<%= b.radio_button %>
	<%= b.label %>
<% end %>

⬇️このようにまとまりごとにdivで囲ったり、class属性をつけることが可能です。class属性以外にも、data属性などHTML属性をつけることができます。

<%= form.collection_radio_buttons(:favorite_food_id, Food.all, :id, :name) do |b| %>
	<div class="radio">
		<%= b.radio_button(class: 'hoge') %>
		<%= b.label(class: 'fuga') %>
	</div>
<% end %>
<div class="radio">
	<input class="hoge" type="radio" value="1" name="user[favorite_food_id]" id="user_favorite_food_id_1">
	<label class="fuga" for="user_favorite_food_id_1">ぎょうざ</label>
</div>
<!-- 以下省略 -->

⬇labelタグの中にラジオボタンと名称を入れることも可能です。

<%= form.collection_radio_buttons(:favorite_food_id, Food.all, :id, :name) do |b| %>
	<%= b.label(class: 'hoge') { b.radio_button(class: 'fuga') + b.text }  %>
<% end %>
<label class="hoge" for="user_favorite_food_id_1">
	<input class="fuga" type="radio" value="1" name="user[favorite_food_id]" id="user_favorite_food_id_1">ぎょうざ
</label>
<!-- 以下省略 -->

紹介しておいてなんですが、凝ったデザインのラジオボタンを作るためにcollection_radio_buttonsヘルパーで上記のように工夫してなんとかするよりも、radio_buttonヘルパーを使ったほうがシンプルだし可読性も良いのではないかとも思います。

ヘルパーを使っていれば意識しなくても大丈夫ですが、labelタグはラジオボタンとセットで実装する必要があります。テキストをクリックしたときにもチェックが入るようにするためです。ラジオボタンとlabelは以下2つのどちらかの方法で対応を取る必要があります。

①labelタグでラジオボタンを囲む
②labelタグのfor属性でラジオボタンのidを指定

モデルではない、ただの配列からラジオボタンを生成する

これは裏技チックなやり方ですが、collection_radio_buttonsはモデルでなくても配列から複数のラジオボタンを生成することも可能です。

<%- fruits = [
  [1, 'バナナ'],
  [2, 'みかん'],
  [3, 'いちご'],
] %>
<%= form.collection_radio_buttons(:favorite_food_id, fruits, :first, :second) %>

このようにモデルにはない食べ物のラジオボタンが生成できました。


なんでこんなことが可能かというと、collection_radio_buttonsの引数の仕様を見ると以下のようになっています。

form.collection_radio_buttons(メソッド名, コレクション, value, ラベル)

引数の「value」と「ラベル」に指定するのは、「コレクション」に存在するメソッド名にすれば良いようです。今回はコレクションはネストした配列で、Arrayのfirstメソッドでvalueを設定し、secondメソッドでラベルを設定している…という仕掛けです。

もちろんこんな裏技を使わなくてもradio_buttonヘルパーで実装してもOKです。あくまで参考程度に。

radio_button_tagヘルパー

フォームではないけどラジオボタンを使いたい場合はradio_button_tagヘルパーを使います。

<%- Food.all.each do |food| %>
	<label><%= radio_button_tag(:food_name, food.id) %><%= food.name %></label>
<%- end %>

⬇出力されるHTMLです。

<label><input type="radio" name="food_name" id="food_name_1" value="1">ぎょうざ</label>
<label><input type="radio" name="food_name" id="food_name_2" value="2">たまごかけごはん</label>
<label><input type="radio" name="food_name" id="food_name_3" value="3">ラーメン</label>
<label><input type="radio" name="food_name" id="food_name_4" value="4">たらこスパゲティ</label>

ちょっと悩むのが、フォームに紐付かないならヘルパーのメリットがそんなにないのではないかと思ってしまう点ですが、ヘルパーを使ったほうが見た目が多少すっきりするという理由で私は使っています。

参考サイト

radio_button | Railsドキュメント
ラジオボタンを生成 。オプションや使い方の例などを多く載せて説明しています。
collection_radio_buttons | Railsドキュメント
モデルからラジオボタンを自動生成 。オプションや使い方の例などを多く載せて説明しています。
radio_button_tag | Railsドキュメント
汎用的なラジオボックスを生成 。オプションや使い方の例などを多く載せて説明しています。
タイトルとURLをコピーしました