check box

collection_check_boxes

APIドックによると以下の引数を持つ。

collection_check_boxes (ActionView::Helpers::FormOptionsHelper) - APIdock
collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)

具体的には以下のように用いるらしい。

collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
   b.label(:"data-value" => b.value) { b.check_box + b.text }
end

第1引数はobjectを渡すので、paramsに格納する名称を指定していると思います。
第2引数はmethodとのことですが、これだけではよく意味が分かりません。
後述しますが、paramsの第1引数キーに対するバリューになるようです。
第3引数はcollectionしたい要素です。これは分かりやすい。
第4引数はvalueとのことなので、第3引数で取り出したオブジェクトから取り出す情報ですね。
第5引数はチェックボックスにラベルを張りたい時に利用するみたいです。

まとめるとcollection_check_boxを使うと以下のようなparamsが返ります。
params { “第1引数”=>{“第2引数”: [第4引数の配列]} }

具体的な中身で言うと、以下のようになります。
params { “post”=>{“author_ids”: [id1, id2, id3 …]} }

なおform_for等のインスタンスをオブジェクトに持つ場合には第1引数は省略できます。
例 f.collection_check_boxes

ここからは疑問と解説です。

第2引数はいったい何を指すの?

以下のように定義してpryでparamsの内容を確認してみました。

= form_for @group do |f|
  = f.text_field :name, placeholder: "グループ名を入力してください"
  = f.collection_check_boxes :user_ids, User.all, :id, :name
  = f.submit :commit, value: "登録する", "data-disable-with": '送信中'

返ってきたparamsがこちら。

pry> params
  => <ActionController::Parameters {
    "utf8"=>"✓", 
    "authenticity_token"=>"wmjtg==", 
    "group"=>{
        "name"=>"hogehoge", 
        "user_ids"=>["", "1", "2"]
        }, 
    "commit"=>"登録する", 
    "controller"=>"groups", 
    "action"=>"create"
    } 
    permitted: false>

paramsの:groupに ”user_ids”=>[“”, “1”, “2”] と書いてあるのがcheckboxで選択したID。 
checkboxの第2引数がparams内のgroupキーのvalue内に、キー名として入っています。

この第2引数は上の例だけ見ると単にname属性の指定のように見えるのですが、どうも制約があるようで、適当に名前を付けるとNoMethodErrorがでます。
第1引数のオブジェクトに利用可能なメソッド名でないと弾かれるのではないかと推察していますが、事実のほどはよくわかりません。根拠となるような記載が見つからなかったです。

params.permitの書き方

上記の例でpermitする項目を抜粋。permit(:name, { user_ids: [] })

pry> params.require(:group).permit(:name, { user_ids: [] })
=> <ActionController::Parameters {
  "name"=>"e3333333333", 
  "user_ids"=>["", "1", "2"]
  } 
  permitted: true>

permitの中身にハッシュの{}があるのが気持ち悪くて、:user_idsじゃダメなのかと思ったけれど、unpermitted parameterと言われてしまいました。
配列には配列であることを明示しないと受け付けてくれない感じです。

pry> params.require(:group).permit(:name, :user_ids)    
Unpermitted parameter: user_ids
=> <ActionController::Parameters {"name"=>"e3333333333"} permitted: true>

user_ids:[]として見たら通りました。
user_ids: []と半角スペースでも問題なくOK。

pry> params.require(:group).permit(:name, user_ids:[])
=> <ActionController::Parameters {
"name"=>"e3333333333", 
"user_ids"=>["", "1", "2"]
} permitted: true>

ここまで書いて気づいたけど、railsはハッシュの{}は省略できるんだったわ。
railsguideも一応確認。permitで配列やハッシュであることを明示してあげると、その通りの形式でないと受け取らないようになっているようです。

Action Controller の概要 - Railsガイド
本ガイドでは、コントローラの動作と、アプリケーションのリクエストサイクルでコントローラがどのように使われるかについて説明します。セッション、フィルタ、cookie、データストリーミング、リクエストによって発生する例外、その他多くの話題を取り扱っています。
ネストしたパラメータ

そしてもう1つ。
ハッシュを指定する場合に省略したいのであれば、permitの最後に記載する必要があります。
何故途中の要素で{}を省略したらダメなのかは分かりませんが、通らないので{}を付けざるを得ません。

params.require(:group).permit( {user_ids: []}, :name)
これは通る

params.require(:group).permit( user_ids: [], :name)
これは通らない
syntaxエラー unexpected ')' で止まる。

params.require(:group).permit(:name, user_ids:[])
これが通るのだから通りそうなものなのに。
配列指定の後に続けて書く場合は{}で囲わないとエラーになっちゃいますね。

書き方例

do以下を省略してもチェックボックスとテキストは出力されます。

= form_for @user do |f|
  = f.collection_check_boxes :company_ids, Company.all, :id, :name 
  = f.submit :commit, value: "登録する", "data-disable-with": '送信中'
= form_for @user do |f|
  = f.collection_check_boxes :company_ids, Company.all, :id, :name do |b|
    = b.label { b.check_box + b.text }
  = f.submit :commit, value: "登録する", "data-disable-with": '送信中'

こんな風に分けて書くこともできる。
クラスの指定も分かりやすいのでこれがおすすめかも。

= form_for @user do |f|
  = f.collection_check_boxes :company_ids, Company.all, :id, :name do |b|
    = b.check_box(class:"form-check-input")
    = b.label(class:"form-check-label"){b.text}
  = f.submit :commit, value: "登録する", "data-disable-with": '送信中'

コメント