How to neatly handle multiple checkboxes with Rails

E

Edgardo Hames

This weekend, I have a written my first Rails application to handle
technical reports at school. The model is very simple, and the only
part I don't know how to deal with is the association between authors
and reports.

Right now, the edit view for the reports has a table with all the
authors in the database and a checkbox for each one of them. If the
list of authors grows too big, this part is going to be a bit too
rough and unusable. How can I handle this "The Rails Way"?

I thought a very neat solution would be to use an input field like the
ones GMail has for the address book. Is that difficult to implement
using Rails and Ajax? Any ideas on how to do that? Are any ActionView
helpers available to do this? How can I dinamically add more input
fields (pushing an "add author" button)?

In case this solution is way too far from being done, how can I handle
checking and unchecking the different authors in that table on the
Controller side? What I would need to do is remove the unchecked
authors and add the checked ones. This involves iterating over the
collection of authors and checkboxes and compare the previous values
with the current ones (which may render the code a little bit
complicated). How do you all usually solve this problem? Is there any
solution for this available in Ruby?

Thanks to everyone,
Ed
--
Encontrá a "Tu psicópata favorito" http://tuxmaniac.blogspot.com

"Tener una amiga en Ginebra es como tener quinotos en almibar o uvas en ron."
"Programming is like sex... make one mistake, and support it the rest
of your life."
"Defeat is an accomplishment not even the best of us could achieve."
 
G

Gavin Kistner

In case this solution is way too far from being done, how can I handle
checking and unchecking the different authors in that table on the
Controller side? What I would need to do is remove the unchecked
authors and add the checked ones. [...] How do you all usually solve
this problem?

The functionality of checkboxes is the same as that of a
multiple-select field, though the UI is different.

I have two tables in a habtm relationship; following is the
(hacked-together) code that I've used (which happens to use selects,
but which would be easy to modify for checkboxes).

The short answer is:
When you are saving, get a list of all the items currently stored for
the relationship into an array, get the list of all the new items
desired into a new array, and then do a two-way difference to figure
out which relationships must be removed and which must be added.


Here's the relevant parts of my code:

# POSTGRESQL SCHEMA
create table roles (
id serial primary key,
name varchar(100) not null
);

create table acts (
id serial primary key,
action_path varchar(100) not null
);

create table acts_roles (
act_id integer references acts on delete cascade,
role_id integer references roles on delete cascade,
primary key ( act_id, role_id )
);


#FILE app/model/act.rb
class Act < ActiveRecord::Base
has_and_belongs_to_many :roles
end


#FILE app/model/role.rb
class Role < ActiveRecord::Base
has_and_belongs_to_many :acts
has_and_belongs_to_many :users

#This really should be done for all habtm, IMO
def users=( new_array )
self.remove_users( self.users - new_array )
self.users << ( new_array - self.users )
self.users
end

#This really should be done for all habtm, IMO
def acts=( new_array )
self.remove_acts( self.acts - new_array )
self.acts << ( new_array - self.acts )
self.acts
end
end


#FILE app/controllers/role.rb
class RoleController < ApplicationController
def edit
@role = Role.find_by_id @params[ :id ]
@page_title = "Edit Role '#{@role.name}'"

@all_actions = Act.find_all.sort_by{ |act| act.action_path }
@actions_allowed = @role.acts
end
def update
role = Role.find( role_atts["id"] )
role.acts = @params['role']['acts'].collect{ |id| Act.find id }
role.save
end
end


#FILE app/views/role/edit.rhtml
<!-- snip -->
<fieldset id="actions" class="two_column">
<legend><strong>Actions</strong> permitted by <%[email protected]%></legend>
<select name="role[acts][]" multiple="multiple"
size="<%=select_length%>">
<%=options_from_collection @all_actions, @role.acts, :action_path %>
</select>
</fieldset>


#FILE app/helpers/application.rb
module ApplicationHelper

# Creates a list of HTML option tags based on a collection of model
instances
# with the ability to provide a second collection of elements which
should be selected
def options_from_collection( all_items, selected, text_method=:name,
value_method=:id )
use_include = selected.respond_to?( :include? ) && !selected.is_a?(
String )

all_items.inject([]) do |options, element|
val = element.send( value_method )
text = element.send( text_method )
is_selected = use_include ? selected.include?( element ) : val ==
selected
if is_selected
options << "<option value=\"#{html_escape(val.to_s)}\"
selected=\"selected\">#{html_escape(text.to_s)}</option>"
else
options << "<option
value=\"#{html_escape(val.to_s)}\">#{html_escape(text.to_s)}</option>"
end
end.join( "\n" )
end
end
 
M

Michael Campbell

In case this solution is way too far from being done, how can I handle
checking and unchecking the different authors in that table on the
Controller side? What I would need to do is remove the unchecked
authors and add the checked ones. [...] How do you all usually solve
this problem?
=20
The functionality of checkboxes is the same as that of a
multiple-select field, though the UI is different.


Quite a bit, yeah. Gavin, I'm old, so go slow, but you've completely lost =
me.

If I iterate over a check_box tag (using a render_partial_collection),
what I get is something like:

<input type=3D"checkbox" name=3D"object[method]" value=3D"2"> foo
<input type=3D"checkbox" name=3D"object[method]" value=3D"3"> bar
<input type=3D"checkbox" name=3D"object[method]" value=3D"4"> baz
<input type=3D"checkbox" name=3D"object[method]" value=3D"10"> blurfle
...

but if I select multiple, params has only 1, due to the same key in each on=
e:

@params =3D> {object =3D> {method =3D> 2}} (or 3, or 4, or 10, but never
more than one of those)

How do I structure the checkbox tag (or can I?) so that I can end up
with some indication that (ex.) both "foo" and "bar" were checked?
 
C

Chris Eidhof

If I iterate over a check_box tag (using a render_partial_collection),
what I get is something like:
<input type="checkbox" name="object[method]" value="2"> foo
<input type="checkbox" name="object[method]" value="3"> bar
<input type="checkbox" name="object[method]" value="4"> baz
<input type="checkbox" name="object[method]" value="10"> blurfle
...
but if I select multiple, params has only 1, due to the same key in each one:
I think the name should be object[method][] or something like that,
check this out: http://www.eye.cc/fortopic941.html
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top