Sterling Rose Design Blog
Dynamic form with a many-to-many relationship
Authored by Dana Jones
May 30, 2009 00:57
1 Comments
Tags: Javascript Rails associations
Authored by Dana Jones
May 30, 2009 00:57
1 Comments
Tags: Javascript Rails associations
My customer’s application is an in-house ordering system (among other things). The order form has a drop-down list to select the client placing the order. It also has two other drop-down lists: primary_contact_id and secondary_contact_id, both of which are based on the Contact model. My customer is certain he will only ever need to associate two contacts with an order, so I’m comfortable with this approach.
The sticking point was that I only want to populate the primary and secondary contact select boxes with contacts that are associated with a particular client. Client and Contact have a many-to-many association.
Ryan Bates’ Dynamic Select Menus Railscast got me most of the way there, but his example (countries/states) was one-to-many, not many-to-many. Following was my adaptation:
So that’s it in a nutshell. Most of the code is Ryan’s (and huge thanks to him for all his help along the way), but there are enough tweaks I thought it warranted documenting.
CommentsThe sticking point was that I only want to populate the primary and secondary contact select boxes with contacts that are associated with a particular client. Client and Contact have a many-to-many association.
Ryan Bates’ Dynamic Select Menus Railscast got me most of the way there, but his example (countries/states) was one-to-many, not many-to-many. Following was my adaptation:
views/javascripts/dynamic_contacts.js.erb
var contacts = new Array();
<% for contact in @contacts -%>
<% for client in contact.clients %>
contacts.push(new Array(<%= client.id %>, '<%=h contact.name %>', <%= contact.id %>));
<% end -%>
<% end -%>
function clientSelected() {
client_id = $('order_client_id').getValue();
options = $('order_primary_contact_id').options;
options2 = $('order_secondary_contact_id').options;
options.length = 1;
options2.length = 1;
contacts.each(function(contact) {
if (contact[0] == client_id) {
options[options.length] = new Option(contact[1], contact[2]);
options2[options2.length] = new Option(contact[1], contact[2]);
}
});
if (options.length == 1) {
$('contact_fields').hide();
} else {
$('contact_fields').show();
}
}
document.observe('dom:loaded', function() {
$('contact_fields').hide();
$('order_client_id').observe('change', clientSelected);
});views/orders/_form.html.erb
<% javascript 'dynamic_contacts' %>
...
<p><strong><%= f.label :client_id, "Customer" %></strong> <%= f.select :client_id,
@clients.map {|c| [c.name, c.id]}, {:prompt => "Select a Customer"} %></p>
<div id="contact_fields">
<p><%= f.label :primary_contact_id, "Primary Contact" %> <%= f.select
:primary_contact_id, @contacts.map {|c| [c.name, c.id]}, :prompt => "Select the
Primary Contact" %></p>
<p><%= f.label :secondary_contact_id, "Secondary Contact" %> <%= f.select
:secondary_contact_id, @contacts.map {|c| [c.name, c.id]}, :prompt => "Select the
Secondary Contact" %></p>
</div>controllers/javascripts_controller.rb
def dynamic_contacts
@contacts = Contact.all
endhelpers/application_helper.rb
def javascript(*files)
content_for(:head) { javascript_include_tag(*files) }
endSo that’s it in a nutshell. Most of the code is Ryan’s (and huge thanks to him for all his help along the way), but there are enough tweaks I thought it warranted documenting.


Posted June 16, 2009 by Adam Smith