Starting from Rails 4.0, you can use a special form options helper called #collection_check_boxes
. It behaves similar to
#collection_select
Show archive.org snapshot
, but instead of a single select field it renders a checkbox and a label for each item in the collection.
= form_for @post do |form|
= form.collection_check_boxes :author_ids, Author.all, :id, :name_with_initial
How generated form params look like
If you have authors with the IDs 1, 2 and 3, the check boxes above will be named like this:
<input type="checkbox" name="post[author_ids][]" value="1">
<input type="checkbox" name="post[author_ids][]" value="2">
<input type="checkbox" name="post[author_ids][]" value="3">
<input type="hidden" name="post[author_ids][]" value="">
Note the hidden input at the end. This is a trick that Rails uses so users can uncheck all the check boxes. If this hidden input wasn't there, the form param author_ids[]
would be missing from the params
entirely, and the list would not be set on your model.
In your Rails controller, this will create params
like this:
{ 'post' => { 'author_ids' => ['1', '2', '3', ''] } }
Permitting array params in your controller
Note that you need some special syntax to whitelist an array-valued parameter in your controller.
This will not work:
params[:post].permit(:subject, :body, :author_ids)
In your server log you will see a line:
Unpermitted parameters: author_ids
You need to say this instead:
params[:post].permit(:subject, :body, :author_ids => [])
Also, you might need to remove the blank when processing the parameter. You can do so like this:
p = params...
p[:author_ids].reject!(&:blank?) # Remove Rails' blank collection item
Customizing the output
You can customize the output by passing a block. The block will be called with a special builder object that has a handful of special methods. See the complex example below.
form.collection_check_boxes :author_ids, Author.all, :id, :name_with_initial do |b|
b.label { b.check_box }
# or
b.label(class: "my-#{ b.object.class.name.parameterize }", 'data-value': b.value) { b.check_box + b.text }
end
The return values of each call will be joined and returned. You may of course render HTML inside the block:
# _fields.html.haml
= form.collection_check_boxes :author_ids, Author.all, :id, :name_with_initial do |b|
.my-checkbox-wrapper
= b.label do
= b.text
= b.check_box
Radio buttons
There is also
#collection_radio_buttons
Show archive.org snapshot
.