How can I make a check_box_tag to post a 'false' or '0' parameter when unchecked?

45,442

Solution 1

check_box (w/o _tag) helper adds hidden field to address your problem for you:

<%= check_box 'object', 'boolean_attribute', {}, 'true', 'false' %>

# result:
<input name="object[boolean_attribute]" type="hidden" value="false" />
<input id="object_boolean_attribute" name="object[boolean_attribute]" type="checkbox" value="true" />

UPD: Dealing with nested resources (Product accepts_nested_attributes_for :line_items)

= form_for @product, url: '' do |f|
  %p
    = f.label :title
    = f.text_field :title

  = f.fields_for :line_items do |li|
    = li.check_box :approved
    = li.label :approved, li.object.id
    %br
  = f.submit

Checking 2 of 3 checkboxes gives me the params as this:

{..., "product"=>{"title"=>"RoR book", "line_items_attributes"=>{"0"=>{"approved"=>"0", "id"=>"47"}, "1"=>{"approved"=>"1", "id"=>"48"}, "2"=>{"approved"=>"1", "id"=>"51"}}}, "commit"=>"Update Product", "action"=>"action1", "controller"=>"test"}

params as YAML for readability:

product:
  title: RoR book
  line_items_attributes:
    '0':
      approved: '0'
      id: '47'
    '1':
      approved: '1'
      id: '48'
    '2':
      approved: '1'
      id: '51'

See? No hidden fields but checked/unchecked states are clearly distinguished.

Having this params allows me to use one line of code to update associated line_items:

@product.update_attributes params[:product]

I hope it helps.

Solution 2

You could use a hidden field above the checkbox:

<%= hidden_field_tag 'object[boolean_attribute]', nil %>

This way, even if your checkbox isn't checked, you'll still get nil submitted. Would that work for you?

Solution 3

If anyone have column type boolean and using check_box_tag then look at this. It worked for me. <%= hidden_field_tag 'order[preparations_attributes][][cooked]', 'false' %> <%= check_box_tag 'order[preparations_attributes][][cooked]', true, preparation.cooked? %>

Solution 4

in my rails app I needed to add single quotes around the true and false.

Original code

<%= f.check_box :admin, {}, true, false %>

Updated Code

<%= f.check_box :admin, {}, 'true', 'false' %>

I hope that helps somebody else!

Share:
45,442
Darme
Author by

Darme

Helping the best teachers find their next students and vice-versa. Corsidia.com

Updated on July 09, 2022

Comments

  • Darme
    Darme almost 2 years

    With the following check_box_tag:

    <%= check_box_tag 'object[boolean_attribute]', 1, object.boolean_attribute %>
    

    I can update the boolean_attribute in only one direction: from false to true.

    When is unchecked by default (because object.boolean_attribute is false) and I check it and then submit the form, a :boolean_attribute => 1 parameter is posted.

    But, when I try to update from true to false no param is passed, so the boolean_attribute remains true.

    In other words, when is checked by default (because object.boolean_attribute is true) and I uncheck it and then submit the form, a :boolean_attribute => 0 is not posted.

    How can I make this check_box_tag to post a :boolean_attribute => 0 parameter when unchecked?

    From the api I can't figure out if there is some option to pass to easily achieve it: http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-check_box_tag

    Thank you.

    EDIT

    For some reason I cannot fathom, in my actual code (with a nested many-to-many association) the hidden_field_tag is not working.

    <%= hidden_field_tag 'order[preparations_attributes][][cooked]', nil %>
    <%= check_box_tag 'order[preparations_attributes][][cooked]', '1', preparation.cooked? %>
    

    Now I have the opposite problem: I can uncheck the checkbox and the preparation is updated as aspected, but if I check the checkbox it messes up the params.

    Here are the posted params for the unchecked box:

    Parameters: {"utf8"=>"✓", "authenticity_token"=>"bGgPGbk+Cuk2q+LEgqetmk4e7xie8dB3iMP9Cj3SUm0=", "order"=>{"customer_name"=>"Duccio Armenise", "duedate"=>"2012-04-25 09:24:00.000000", "preparations_attributes"=>[{"quantity"=>"1", "description"=>"custom recipe", "kind"=>"custom", "cooked"=>"", "recipe_id"=>"9", "id"=>"86", "quantities_attributes"=>[{"ingredient_id"=>"", "qty"=>"", "_destroy"=>"0"}, {"ingredient_id"=>"11", "qty"=>"5.0", "id"=>"193", "_destroy"=>"0"}], "_destroy"=>"0"}], "add_preparation"=>{"recipe_id"=>""}}, "continue"=>"Confirm", "id"=>"31"}
    

    Now see what a mess when I check the checkbox, beginning from "cooked"=>" ", for some reason Rails is closing the preparation_attributes hash too early!

    Parameters: {"utf8"=>"✓", "authenticity_token"=>"bGgPGbk+Cuk2q+LEgqetmk4e7xie8dB3iMP9Cj3SUm0=", "order"=>{"customer_name"=>"Duccio Armenise", "duedate"=>"2012-04-25 09:24:00.000000", "preparations_attributes"=>[{"quantity"=>"1", "description"=>"custom recipe", "kind"=>"custom", "cooked"=>""}, {"cooked"=>"1", "recipe_id"=>"9", "id"=>"86", "quantities_attributes"=>[{"ingredient_id"=>"", "qty"=>"", "_destroy"=>"0"}, {"ingredient_id"=>"11", "qty"=>"5.0", "id"=>"193", "_destroy"=>"0"}], "_destroy"=>"0"}], "add_preparation"=>{"recipe_id"=>""}}, "continue"=>"Confirm", "id"=>"31"}
    

    EDIT #2:

    I think I ran into a Rails bug related to deep nested resource forms and param passing: https://github.com/rails/rails/issues/5937

    For now I made it to work with a select_tag:

    <%= select_tag 'order[preparations_attributes][][cooked]', options_for_select({yes: 1, no: 0}, preparation.cooked? ? 1 : 0) %> 
    

    I think that switching to a select_tag in order to avoid the "hidden_field gotcha" is an acceptable workaround.

    Anyway, thank you for the answers!

  • jdoe
    jdoe about 12 years
    There is more convenient approach. See my answer.
  • Darme
    Darme about 12 years
    Thank you, I knew this approach, I'm after something else because for some reason in my real app this is not working (I have some nested many-to-many models...). I'm updating the question with my actual code...
  • Darme
    Darme about 12 years
    Thank you but I was after a check_box_tag solution because in my actual code I am updating a nested resource. See my actual code in the edit. Is you solution applicable to a nested resource form as well?
  • jdoe
    jdoe about 12 years
    Yes, it is suitable! Nothing prevents you from passing something like 'order[preparations_attributes][][cooked]' instead of 'object'.
  • Darme
    Darme about 12 years
    For now I made a workaround switching to a select_tag solution (see EDIT#2), but I'm going to test your suggestion as well...
  • Darme
    Darme about 12 years
    ...uhm, I tried as you suggested and with nil instead of 'boolean_attribute' but there are some unwelcome '[]' at the end of the name: <input type="checkbox" value="true" name="order[preparations_attributes][][cooked][]" id="order_preparations_attributes__cooked_"> name should be ="order[preparations_attributes][][cooked]" in order for this to work, any other idea?
  • jdoe
    jdoe about 12 years
    You didn't say a word about the structure of your app (aboute the M in MVC) so I couldn't be more helpful. Let's make it this way: I'll show you the way of dealing with nested resources. It's a classic example: Product has_many :line_items. LineItem has :approved field. Check my UDP to dealing with this fields via check_box.
  • Darme
    Darme about 12 years
    You're right. Since in my actual app I have 3 level nesting (with many-to-many) I tried to boil my problem down to its essential. I knew this approach but in my case I really needed a lower-level one with check_box_tag or select_tag. If you are interested in the bug I think I ran into, here is the link: github.com/rails/rails/issues/5937
  • jdoe
    jdoe about 12 years
    Rails perfectly handles nested fields_for. Using = f.fields_for :line_items do |li_form| gives you li_form which can be used in its turn to build another nesting: = li_form.fields_for :order do |order_form|. Just checked: @product.update_attributes params[:product] works, zeros for unchecked checkboxes appear in params.
  • Darme
    Darme about 12 years
    A refactor attempt using fields_for was already in my todo list, thank you for the hint. The alleged bug is related to a lower-level approach, namely, the use of hidden_field in conjunction to check_box_tag.
  • counterbeing
    counterbeing almost 11 years
    I was just playing with this, and it works nicely, my controller is receiving the params I want. However, it broke my tests because Capybara now can't find the checkbox since there are two checkboxes with the same ID. I'm going to try to target it a different way, but it seems that doing something like this where you end up with two of the same ids on the same page is a bit hackish.
  • Mauricio Moraes
    Mauricio Moraes over 10 years
    You can also verify if you really want to have capybara target your hidden fields. If not, you can set in your spec_helper.rb or equivalent: Capybara.ignore_hidden_elements = false
  • bigtunacan
    bigtunacan about 10 years
    Nice thing about this answer is that it also works with the Formtastic gem, which at the current time has poor support for an individual checkbox.