简体   繁体   中英

Rails Dynamically Changing Collection Select In Form Based on form field

I have a Rails 3.2.14 app that tracks calls and each call has a pickup and dropoff facility from the Facility model which is an association.

In the call model there is an association for the Region model (ie Houston, Dallas, Austin, etc) where we select a region based on where the call is coming from.

What I'd like to do is be able to select a specific region (ie Houston) and in the pickup facility collection only show Houston region facilities.

I'm assuming to start off with I'd need to setup a relationship between Facility and Region as such:

facility.rb

attr_accessible :region_id
belongs_to :region

region.rb

has_many :facilities

Then I'd need to set each facility with a region_id that matches the respective region (ie Houston, Dallas, etc) so the association works properly.

What I'm unsure of is how to select the specific region and only have the facilities in that region display in the form. I'm assuming I would use some jQuery/JS/Ajax here to make it happen but not sure how to make it work.

Here's an excerpt of what my call, facility, and region models currently look like:

call.rb

belongs_to :transferred_from, :foreign_key => :transfer_from_id, :class_name => 'Facility'
belongs_to :transferred_to, :foreign_key => :transfer_to_id, :class_name => 'Facility'
belongs_to :region

facility.rb

has_many :calls_transferred_from, :foreign_key => :transfer_from_id, :class_name => 'Call'
has_many :calls_transferred_to, :foreign_key => :transfer_to_id, :class_name => 'Call'

region.rb

has_many :calls

Here's an excerpt of what my call form partial looks like:

_form.html.erb

<%= form_for(@call) do |f| %>
  <%= f.label :region %>
  <%= f.collection_select(:region_id, Region.all, :id, :area, {:include_blank => true}, {:class => 'select', required: true}) %>
  <%= f.label :Transfer_From %>
  <%= f.collection_select(:transfer_from_id, Facility.active.order("facility_name ASC"), :id, :facility_name, {:include_blank => true}, {:class => 'select'}) %>
 <%= f.button "Submit", class: 'btn btn-info btn-large', data: {disable_with: "<i class='icon-spinner'></i>Processing..."} %> 
<% end %>

If any of this is confusing or needs clarification, please let me know. To summarize, I'm trying to select a region (Houston) and only show the Facilities that are in the Houston region in the form when that region is selected.

Thanks in advance for any help or tips you can provide.

For things like this, I typically keep it real simple by loading a dictionary in the client, and just reference that.

So in your view, you can build out something like this:

<script>
  var facilitiesForRegion = {};
  <% Facility.all.each do |facility| %>
    var region = facilitiesForRegion[<%= facility.region_id %>];
    var object = { id: <%= facility.id %>, name: '<%= facility.facility_name %>'};
    if (region) {
      region.push(object);
    }
    else {
      facilitiesForRegion[<%= facility.region_id %>] = [object];
    }
  <% end %>

  $("#call_region_id").on("change", function (e) {
    var id = parseInt($(this).val());
    var facilities = facilitiesForRegion[id];
    $('#call_transfer_from_id').children().remove();
    for (var i = 0; i < facilities.length; i++) {
      $('#call_transfer_from_id').push('<option value="'+ facilities[i].id +'">'+ facilities[i].name +'</option>');
    }
  });
</script>

It's a little ugly. I'm not the biggest fan of building out an array of data in the view like that, but it works and avoids waiting ajax requests.

Be sure to double check the ids of the drop downs. I was guessing on that, based on field names and the for_for.

Based off of the Railscasts dynamic select menus I've come up with the following:

calls.js.coffee

jQuery ->
  facilities = $('#call_transfer_from_id').html()

  $('#call_region_id').change ->
    region = $('#call_region_id :selected').text()
    options = $(facilities).filter("optgroup[label=#{region}]").html()

    if options
      $('#call_transfer_from_id').html(options)   
    else
      $('#call_transfer_from_id').empty()

_form.html.erb

<%= f.grouped_collection_select :transfer_from_id, Region.order(:area), :facilities, :area, :id, :facility_name, {include_blank: true}, class: 'select' %>

The problem with the above grouped_collection_select is that it's calling :facilities which lists all the facilities, I need to call the equivalent of Facility.active (scope I created on Facility to only list active facilities). How can I call this from within grouped_collection_select?

So far in a new call I'm able to select the region then the facilities for that region show up only in the from field (transfer_from_id being called). When editing the call and trying to change the facility I'm presented with a full list of facilities grouped by region instead of just the facilities for that region. If I want to narrow it down I have to change region then change it back to the original for the specific region's facilities to show up solely.

I think I'm making progress with this, but it's not working out the way I want it. I'm really open to answers that make more sense and are more clear.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM