12

I have a model MyModel with a serialized attribute a, describing an array of symbols.

This code works :

<% form_for @my_model do |f| %>
  <%= f.select :a, MyModel::AS, :multiple => true) %>
<% end %>

The parameters are correct :

{ :my_model => { :a => [:a_value1, :a_value2] } }

I want to transform this multiple select into a set of checkboxes, like this :

<% form_for @my_model do |f| %>
  <% MyModel::AS.each do |a_value|
    <%= f.check_box(:a_value) %>
  <% end %>
<% end %>

It works too, but the parameters are not the same at all :

{ :my_model => { :a_value1 => 1, :a_value2 => 1 } }

I think of 2 solutions to return to the first solution...

  • Transform my check_box into check_box_tag, replace multiple select, and add some javascript to 'check' select values when user clic on check_box_tags. Then, the parameter will be the same directly in the controller.
  • Add a litte code into the controller for 'adapting' my params.

What solution is the less ugly ? Or is there any other one ?

pierallard
  • 3,326
  • 3
  • 21
  • 48

4 Answers4

35

I found a solution, using 'multiple' option that I didn't know.

<% MyModel::AS.each do |a_value| %>
  <%= f.check_box(:a, { :multiple => true }, a_value) %>
<% end %>

Result parameters are a little weird, but it should work.

{"my_model" => { "a" => ["0", "a_value1", "0", "a_value2", "0"] }

Edit from @Viren : passing nil at the end of the function like

  <%= f.check_box(:a, { :multiple => true }, a_value, nil) %>

works perfectly.

pierallard
  • 3,326
  • 3
  • 21
  • 48
  • 7
    Pass `nil` at the end end and it would work as expected check [this](http://apidock.com/rails/ActionView/Helpers/FormHelper/check_box) – Viren Apr 11 '13 at 06:23
  • 1
    Please edit your answer if you want, there is also an typo there. After `<% MyModel::AS.each do |a_value|` need `%>`. Just saying – ksugiarto Jun 22 '13 at 09:41
  • 1
    My personal take on this: this would be an excellent way of defining roles that a user may have. That way, role definitions are hard-coded and you don't need any extra joins or queries to grab other data from the database because it's all there in your serialized field. Awesome! – user3934630 Apr 06 '15 at 21:09
6

There is another solution worth mentioning that makes it very easy to insert records into the database if you have a has_and_belongs_to_many or has_many through relationship by using the collection_check_boxes form helper. See documentation here.

<%= f.collection_check_boxes :mymodel_ids, MyModel::AS, :id, :name do |m| %>
  <%= m.check_box %> <%= m.label %>
<% end %>

Then, in the controller we would allow the mymodel_ids attribute:

params.require(:mymodel).permit(:name, mymodel_ids:[])

We can also set up a model validation to require that at least one of the checkboxes be checked:

validates :mymodel_ids, presence: true

An added benefit of this method is that if you later edit the mymodel record and uncheck one of the checkboxes, its record will be deleted from the many_to_many association table on save.

Vadim
  • 1,916
  • 2
  • 19
  • 39
  • Is this anywhere in the guides? I couldn't find anything. Could you give me the link? Thanks! – Metaphysiker Nov 17 '18 at 19:29
  • 1
    @Metaphysiker, here's a link to documentation: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_check_boxes. – Vadim Nov 18 '18 at 16:50
  • Should the `presence` validation work correctly with this option? Or since `collection_check_boxes` includes an empty string, does the validation pass even though no checkbox has been checked? – Travis Smith Mar 30 '21 at 15:05
  • 1
    @TravisSmith The presence validation definitely works, because Rails treats an empty strings as blank input and will not consider it present. – Vadim Mar 31 '21 at 14:46
3

You can do it like this:

<% MyModel::AS.each do |a_value| %>
  <%= f.check_box("a[]", a_value) %>  
<% end %>

This will make params come to server as follows

{ :my_model => { :a => [:a_value1, :a_value2] } }
Moustafa Samir
  • 2,248
  • 1
  • 25
  • 32
0

if you already have a select_tag

<%= select_tag "filters", options_from_collection_for_select(filter_values, "id", "name", selected_ids), multiple:true, class:"form-control" %>

and wants to replace it with check_box_tag, you will need to implement something like this:

<div class="checkbox-inline">
    <%= check_box_tag "filters[]", value.id, selected_ids.include?(value.id), { :multiple => true} %>
    <%= value.name %>
</div>

notice the ending brackets on the name which is needed to catch check box results in the same parameter.

When I implemented this, the parameters were of the same format between the select_tag and the check_box_tag

Sujeev
  • 276
  • 4
  • 10