Edited at

【Rails】複数のチェックボックスの値を配列でPOSTする【collection_check_boxes】

More than 1 year has passed since last update.


まえがき

※この記事のソースは、筆者が走り書きしたもので実行したものではないので、多分コピペでは動きません。使い方の解説だと思ってよんで下さい。

Ruby on RailsのFormで、複数のチェックボックスの値をまとめて扱いたい状況って、割とよくあるんじゃないかなと思います。

・検索で、都道府県を複数選択

・タグ付けするときに複数タグをチェックボックスで選択

などなど。。。

その際に取れる選択肢は

1. check_box_tag 'prefecture[]'のように、check_box_tagでべた書きする

2. 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がかなりスッキリします。

よかったら使ってみて下さい。

マサカリお待ちしております。


参考にした記事

https://qiita.com/sho012b/items/3a595fde14516081dff5