Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails - Dynamic Select - Collection Select

I've been trying to get some dynamic select functionality working, but despite many different tutorial i've yet to get it to work. For ease of reading, i've brought the code examples down to basics. Any advise would be greatly appreciated.

On the faults page, i need to assign a company and contact to the fault, but I only want to be able to see the contacts associated with the selected company

Fault - belongs_to :company, :user, :contact
User - has_many :faults
Contacts - has_and_belongs_to_many :companies
Company - has_and_belongs_to_many :contacts, has_many :faults

/faults/_form.html.erb

<%= f.label :company, "Company:" %>

<%= collection_select(:fault,:company_id,@companies,:id,:full_name, :prompt => "Please select a company") %></br>

<%= f.label :contact, "Contact:" %>

<%= f.collection_select :contact_id, @contacts, :id, :name, :prompt => "Select a Contact" %>

<%= link_to "Add New Contact", {:controller => "companies", :action => "index"}, :confirm => "To add a contact for a company you need to do this from the companies page." %></br>
like image 423
Freddy Wetson Avatar asked Nov 11 '13 15:11

Freddy Wetson


2 Answers

Gotcha. Using UJS you can dynamically populate your select in 5 steps.

  1. Add class names to your selects in the view
  2. Add JavaScript (or CoffeeScript) to watch the changes on the select element
  3. Create a controller method to fetch the info you need based on your selection
  4. Add a route to match your new controller method
  5. Create a UJS view to update your contacts select

So,

  1. Add class names:

    <%= f.label :company, "Company:" %>
    <%= collection_select(:fault,:company_id,@companies,:id,:name, {:prompt => "Please select a company"}, {:class => "company_selection"}) %>
    <%= f.label :contact, "Contact:" %>
    <%= f.collection_select :contact_id, @contacts, :id, :name, {:prompt => "Select a Contact"}, {:class=>"contact_selection"} %>
    
  2. Throw in some CoffeeScript (app/assets/javascripts/faults.js.coffee)

    $(document).ready ->
      $(".company_selection").on "change", ->
        $.ajax
          url: "/faults/get_contacts"
          type: "GET"
          dataType: "script"
          data:
            company_id: $('.company_selection option:selected').val()
    
  3. Update your faults controller

    def get_contacts
      @company = Company.find params[:company_id]
      @contacts = @company.contacts
    end
    
  4. Add a route to your new method

    resources :faults do
      collection do
        get 'get_contacts', to: "faults#get_contacts"
      end 
    end
    
  5. Add the UJS file (app/views/faults/get_contacts.js.erb)

    $('.contact_selection').empty();
    $('.contact_selection').append( $('<option>Select the Contact</option>'));
    <% @contacts.each do |contact| %>
      $('.contact_selection').append($('<option value="<%= contact.id %>"><%= contact.name %></option>'));
    <% end %>
    
like image 188
trh Avatar answered Oct 19 '22 06:10

trh


Vanilla JS Option

This can be achieved with vanilla javascript only. Make sure that there is a route at companies/[id]/contacts.json which returns correct data.

const select = document.querySelector('#company_id');
select.addEventListener('change',function(e) {
  axios.get(`/companies/${e.target.value}/contacts.json`)
    .then((res) => {
      let contactSelect = document.querySelector('#contact_id')
      contactSelect.innerHTML = ''
      res.data.map((model, i) => {
        contactSelect.options[i] = new Option(model.name, model.id);
      })
    })
});
like image 1
Jeremy Lynch Avatar answered Oct 19 '22 06:10

Jeremy Lynch