check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0") public

Returns a checkbox tag tailored for accessing a specified attribute (identified by method) on an object assigned to the template (identified by object). This object must be an instance object (@object) and not a local object. It’s intended that method returns an integer and if that integer is above zero, then the checkbox is checked. Additional options on the input tag can be passed as a hash with options. The checked_value defaults to 1 while the default unchecked_value is set to 0 which is convenient for boolean values.

Gotcha

The HTML specification says unchecked check boxes are not successful, and thus web browsers do not send them. Unfortunately this introduces a gotcha: if an Invoice model has a paid flag, and in the form that edits a paid invoice the user unchecks its check box, no paid parameter is sent. So, any mass-assignment idiom like

@invoice.update_attributes(params[:invoice])

wouldn’t update the flag.

To prevent this the helper generates an auxiliary hidden field before the very check box. The hidden field has the same name and its attributes mimic an unchecked check box.

This way, the client either sends only the hidden field (representing the check box is unchecked), or both fields. Since the HTML specification says key/value pairs have to be sent in the same order they appear in the form, and parameters extraction gets the last occurrence of any repeated key in the query string, that works for ordinary forms.

Unfortunately that workaround does not work when the check box goes within an array-like parameter, as in

<%= fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
  <%= form.check_box :paid %>
  ...
<% end %>

because parameter name repetition is precisely what Rails seeks to distinguish the elements of the array. For each item with a checked check box you get an extra ghost item with only that attribute, assigned to “0”.

In that case it is preferable to either use check_box_tag or to use hashes instead of arrays.

Examples

# Let's say that @post.validated? is 1:
check_box("post", "validated")
# => <input name="post[validated]" type="hidden" value="0" />
#    <input type="checkbox" id="post_validated" name="post[validated]" value="1" />

# Let's say that @puppy.gooddog is "no":
check_box("puppy", "gooddog", {}, "yes", "no")
# => <input name="puppy[gooddog]" type="hidden" value="no" />
#    <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />

check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
# => <input name="eula[accepted]" type="hidden" value="no" />
#    <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
Show source
Register or log in to add new notes.
October 13, 2010
13 thanks

Sending array parameters

Another technique to use when you need to send an array parameter is pass in the :multiple option.

check_box("puppy", "commands", {:multiple => true}, "sit", nil)
check_box("puppy", "commands", {:multiple => true}, "fetch", nil)
check_box("puppy", "commands", {:multiple => true}, "roll_over", nil)

If all checkboxes are checked, the paramters will be:

"puppy" => {"commands" => ["sit", "fetch", "roll_over"]}

NOTE: because of the gotcha, the hidden fields will be inserted and any unchecked boxes will be sent as “” (empty string). You will need to filter those values out in your model:

class Dog < ActiveRecord::Base
  def commands=(commands)
    commands.reject(&:blank?)
  end
end
June 10, 2009 - (>= v1.0.0)
5 thanks

Have the check_box checked by default

To have the check box checked by default, pass either :checked => true or :checked => 'checked' in the options. See ActionView::Helpers::InstanceTag#to_check_box_tag for details.

October 15, 2009
4 thanks

Have check_box checked by default

In addition to comment below, you can make a column with default value so in your forms it will be enabled by default and behave correctly with validation errors unlike :checked => true

in your migration

add_column :accounts, :ssl_enabled, :boolean, :default => 1
July 23, 2013
3 thanks

Use strings as parameters, not booleans

I just stumbled across this somewhere in our codebase. The first example is faulty, the second one is correct.

= f.check_box :public, {}, true, false
# <input id="event_public" name="event[public]" type="checkbox" value="true" />

and:

= f.check_box :public, {}, "true", "false"
# <input name="event[public]" type="hidden" value="false" />
# <input id="event_public" name="event[public]" type="checkbox" value="true" />
April 8, 2015 - (v3.1.0 - v4.1.8)
1 thank

What object_name.method values will result in checked chekboxes.

*It’s intended that method returns an integer and if that integer is above zero, then the checkbox is checked.* - more exactly, that’s how it’s determined whether value will be checked or not:

(`@checked_value` is checked_value, `value` is what object_name.method returns)

def checked?(value)
  case value
  when TrueClass, FalseClass 
    value == !!@checked_value
  when NilClass
    false
  when String
    value == @checked_value
  else
    if value.respond_to?(:include?)
      value.include?(@checked_value)
    else
      value.to_i == @checked_value.to_i
    end
  end
end
January 15, 2011 - (<= v2.3.8)
0 thanks

Parameter extraction logic changed in Rails 2.3!

What is documented here about Rails parameter extraction always getting the first occurrence of a key is apparently incorrect (in Rails 2.3 it gets the last occurrence).

See the following email thread on [rubyonrails-core]:

http://www.mail-archive.com/rubyonrails-core@googlegroups.com/msg08719.html

I quote:

> In the docs for ActionView::Helpers::FormHelper#check_box, when
> discussing why the method adds a hidden input tag _after_ the checkbox
> tag, it says
>
>  "...Rails parameters extraction always gets the first occurrence of
> any given key..."
>
> I'm not sure this is still the case.

Indeed it changed in 2.3 but unfortunately the patch didn't update the
docs. We noticed this a few days ago, they are correct now in edge.