Sunday, December 7, 2008

Multi-select boxes and Rails

Using a multi select input control to manage a one-to-many relationship in Rails is not hard, but it also was not obvious to me when I first tried to do it.

Have two models that are related as so:

class ControlGroup < ActiveRecord::Base
has_many :control_group_rate_addresses
has_many :rate_addresses, :through => :control_group_rate_addresses
end

class RateAddress < ActiveRecord::Base
has_many :control_group_rate_addresses
has_many :control_groups, :through => :control_group_rate_addresses
end

class ControlGroupRateAddress < ActiveRecord::Base
belongs_to :control_group
belongs_to :rate_address
end


And a fragment of controller code in ControlGroupsController:

def edit
@control_group = ControlGroup.find(params[:id])
@all_rate_addresses = RateAddress.all
end


Along with an html fragment of:
<%= f.label :rate_address_id %>

<%= collection_select(:control_group, :rate_address_ids, @all_rate_addresses, :id, :name, {}, :multiple => true) %>


The part that I didn't get immediately was the ":rate_address_ids" method call.

I also have ajax interaction with this controller that needs to know what items have been selected and make a call back to the server when the multi-select input control changes. I use jquery, but you could do this (although it would be more verbose) without it. The relevent lines are:

var rate_addresses = $.map($("#control_group_rate_address_ids option[selected]"),
function(value) { return $(value).attr('value') });

$.get(device_url(node_id), { "rate_address_ids[]": rate_addresses },
function(data){
$("#device-list-container").html(data);
});


Building the array of selected RateAddress id's was pretty easy using jquery's powerful selectors.

Then I just had to append the [] in the get call to ensure that the parameters came through as an array.

Back in the controller I can just look at the params and pull out the selected rate_addresses and do whatever I wish.

Hope this helps someone else through this little frustration I had.