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 <%=@role.name%></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
···
On Apr 26, 2005, at 1:54 PM, Edgardo Hames wrote:
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?