In two projects now I’ve had to use code where I update a select box (machines) in regards to what’s selected in the first (customers). mark[oz] from #rubyonrails asked how to do this last week and I told him I would write a blog post the following night. 5 days later, here it is.

I use this when I’m creating a new call and the user needs to select which customer and then which machine. I have the following in my CallsController:

def new
  @call = Call.new
  @customers = Customer.find(:all, :order => "name ASC")
end

Relatively simple. We instantiate a new call object so the form knows where to go, and we define @customers so the first select box has some information.

Of course in our Customer model we have:

class Customer < ActiveRecord::Base
  has_many :machines
end

And the machine model:

class Machine < ActiveRecord::Base
  belongs_to :customer
end

And the call model:

class Call < ActiveRecord::Base
  belongs_to :machine
  belongs_to :customer

These define the relationships used within the system.

Next is the new.html.erb page itself.

New Call
<% form_for @call do |f| %>
  <%= render :partial => "form" %>
  <%= submit_tag "Create" %>
<% end %>

And a stripped-down version of the form partial:

<%= f.label "customer_id" %>
<%= f.select "customer_id", @customers.map { |c| [c.name, c.id] }%>
<%= observe_field "call_customer_id", :url => by_customer_machines_path, :with => "customer_id" %>
<%= f.label "machine_id" %>
<%= f.select "machine_id", "Please select a customer" %>

Right now that we have the form all set up, lets set up our machines controller

class MachinesController < ApplicationController
#CRUD operations go here

def by_customer
  @machines = Machine.find_all_by_customer_id(params[:customer_id])
end
end

And our by_customer.rjs:

page.replace_html 'call_machine_id', options_from_collection_for_select(@machines, "id", "name")

And finally the config/routes.rb:

map.resources :customers do |customer|
  customer.resources :machines
end

map.resources :machines, :collection => { :by_customer => :post }

Now when we select a customer from the top drop down, the machines drop down should be populated too.