まえがき
Ruby on RailsのFormで、複数のチェックボックスの値をまとめて扱いたい状況って、割とよくあるんじゃないかなと思います。
・検索で、都道府県を複数選択
・タグ付けするときに複数タグをチェックボックスで選択
などなど。。。
その際に取れる選択肢は
-
check_box_tag 'prefecture[]'
のように、check_box_tagでべた書きする -
collection_check_boxes
を使う
が主になるかなと思います。
せっかくRailsを利用する以上、form_for(Rails5ではform_withも)のhelperに乗っかっていきたい!
ということで、2のcollection_check_boxesを使う方法について記載します。
ないよう
collection_check_boxesについて
まず、collection_check_boxesですが、
・form_forのビルダーを用いるもの
collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
・そのまま使えるもの
collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
2種類があります。違いは、第一引数にobjectを受けるかどうかです。
form_forのビルダーを用いるものの場合は、第一引数にform_forのヘルパー内で保持しているオブジェクトが自動で渡されるため、省略されています。
引数の解説
object
フォーム全体の更新対象になるオブジェクトです。
オブジェクトは、特に何も指定しない場合の初期値の設定に用いられます。
method
objectに対して、この一連のチェックボックスが対応するメソッドを指定します。
そのため、objectにこのメソッドが実装されていないと、エラーで落ちます。
ここは、初期値の指定と、特に指定しない場合チェックボックスタグのnameの値(つまりパラメータのkey)に使われます。
object.methodの値が下のvalue_methodの結果と一致していると、checked状態になります。
collection
実際にチェックボックスを生成する際に使われるcollectionです。
配列とか、ActiveRecord::Relationならはいるっぽいです。
なので、第一引数、第二引数と全く無関係のオブジェクトでも動きます。
value_method, text_method
ほぼ一緒なのでまとめて。
collectionの各要素に、対して呼ばれるメソッドです。
collectionの要素数分チェックボックスが作られるわけですが、それぞれのtext、valueに設定される値がこのメソッドを呼んだ結果になります。
options, html_options
ちょっと多い上に基本的な使い方は他のview_helperと変わらないので、省略します。
初期選択とか、値を0,1じゃなくす、idとかclassを加える、とかはこのへんでやります。
&block
生成されるチェックボックスの形とかラベルを変更したいときに使います。省略するとcollection.textがラベルにくっつきます。
気力があれば別で書きます。
おおまかな使い方
ActiveRecordの関連が
object >-< collection
的な感じだとかなり楽に使えます。
例えば、あるユーザーに複数の都道府県を関連付ける、みたいなとき
User >-< Prefecture
class User
has_many :user_prefectures
has_many :prefectures, through: :user_prefectures
end
class UserPrefecture
belongs_to :user
belongs_to :prefecture
end
class Prefecture
# 都道府県名がnameに入っているとする
has_many :user_prefectures
end
みたいな感じだとして、controllerから更新対象のUserが@user
に格納されて渡されているとすると、
<%= form_for @user |f| do %>
<%= f.collection_check_boxes :prefectures, Prefecture.all, :id, :name %>
<% end %>
のような形でかけます。
結果は(いろいろ省略しますが)、
<form>
<input type="hidden" name="user[prefectures][]" value="">
<input type="checkbox" value="1" name="user[prefectures][]" id="user_prefecturs_1">
<label for="user_prefecturs_1">北海道</label>
<input type="checkbox" value="2" name="user[prefectures][]" id="user_prefecturs_2">
<label for="user_prefecturs_1">青森</label>
<!--
省略
-->
<input type="checkbox" value="47" name="user[prefectures][]" id="user_prefecturs_47">
<label for="user_prefecturs_47">沖縄</label>
</form>
みたいなのができるはずです。
※もし、DBにない値をチェックボックスの候補で使いたい場合
いろいろやり方はあるとは思いますが、ActiveModelを用いて、ActiveRecordっぽいクラスを作っちゃうのが楽です。
class Prefecture
include ActiveModel::Model
attr_accessor :id, :name
# 長くなるんで全部は書きません
ALL = %w[北海道 青森 秋田].map.with_index(1) {|name, index| new(id: index, name: name)}.freeze
end
みたいなクラスを作って、collectionにこいつを指定してあげればよいです。
検索フォームなどだと値をDBに保存しないので、FormObjectを使ったりすると思うので、FormObjectにprefecturesメソッドを実装してあげればいい感じになります。
(ちょっと気力がないので、気力があれば追記します。)
おわりに
collection_check_boxes、うまく使うとviewがかなりスッキリします。
よかったら使ってみて下さい。
マサカリお待ちしております。
参考にした記事