0

I have a Rails 3.2.20 app which I have a form setup that selects a facility which has a facility_name and facility_address field in the facility model. This is a relationship between the Call and Facility model to allow facilities to be assigned to calls.

Currently I'm able to select a facility and assign it no problem by selecting the name from a dropdown/search using select2. But what I want to be able to do is when selecting the facility have a small div display next to it with the facility_address for the specific facility.id. So when I select 24 hour emergency room, a small div will show the address for the facility.

I'm unsure as to how to do this with jQuery or CoffeeScript as I'm a bit rusty. Attached is a screenshot of what my form looks like. Below is what I have so far to handle the jQuery, but I need to figure out a way to pass a data attribute to grouped_collection_select so I can get the address data field to display with jQuery. Whenever I select a facility the div will pop out with "undefined"

I'm not sure if I can even pass a data attribute like data: {address: :facility_address}. Trying this yields errors. There's go to be some way to do this. Or perhaps restructuring it to a select form helper and use group_options_for_select

enter image description here

I'm doing searches on how to display data from a form field using jQuery or CoffeeScript but so far I haven't found anything specific.

If you need code examples of my form or more data about my models relationships, please let me know. But basically I just need to display a facility address once the facility.id is selected in the form.

_form.html.erb

<%= f.grouped_collection_select :transfer_from_id, Region.order(:area), :active_facilities, :area, :id, :facility_name, {include_blank: true}, class: 'select' %><div id ="transfer-from-address"></div>

region.rb

class Region < ActiveRecord::Base
  attr_accessible :area
  has_many :calls
  has_many :facilities

  def active_facilities 
    self.facilities.active
  end
end

calls.js.coffee

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

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

    if options
      # Set the options and include a blank option at the top
      $('#call_transfer_from_id').html("<option value=''></option>" + options)
      # Ensure that the blank option is selected
      $('#call_transfer_from_id').attr("selected", "selected")
    else
      $('#call_transfer_from_id').empty()

  $('#call_region_id').change ->
    update_facilities()

  update_facilities()

  jQuery ->
  $("#call_transfer_from_id").on "change", (e) ->
    selected = $("#call_transfer_from_id option:selected")
    $("#transfer-from-address").html("""
    <h3>#{selected.data("address")}</h3>
    """)
nulltek
  • 3,247
  • 9
  • 44
  • 94

3 Answers3

1

There are several ways to accomplish this, but none using the default grouped_collection_select. That method takes both an option_key_method and an option_value_method, but these are intended as basic getters on the collection member to fill in the content of the option tag, not replace the option tag in entirety. I see three primary options

create your own select tag builder

Write your own select builder that uses content_tag to build your own option tag inside the select, something similar to:

content_tag(:option, facility.name, value: facility.id, data: {address: facility.address})

[EDIT] - adding a simple select builder as the OP asked.

Assuming a helper as defined.

  def select_with_data_attributes(addys)
    content_tag(:select, {}) do
      raw addys.map {|a| content_tag(:option, value: a[1], data: {address: a[0]}) { a[0] } }.join
    end
  end

you could render this in your view with:

<%= select_with_data_attributes(@addresses.map{|a| [a.address, a.id]}) %>

This will construct html that looks like:

<select>
  <option data-address="123 elm st" value="1">123 elm st</option>
  <option data-address="345 elm st" value="2">345 elm st</option>
</select>

I pretty much disagree with this approach though, because you'll end up working against form builder libraries like simple_form and the built in helpers for rails as well.

do an ajax request for the address

This would be a better choice (especially if its a really long list) since it feels kinda ugly to be pushing this stuff into the option element, and it can take a long time to render that much html. You could make a full GET request to a server endpoint.

$.get("/facilities/#{selectedOption}/address").done(...etc)

Dynamically populating your selects via server requests as the user chooses options is the only advisable method once you get a fair number of facilities and locations, so this direction would be the biggest refactor from your current code, but potentially the best long term solution.

Render the data in the view to support your form

This would be my choice for small amounts of dynamic data. The idea would be to render a custom script tag with the addresses serialized into a data structure.

Based on the sample html from the api docs here: http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/grouped_collection_select

I put together a little jsbin showing a rough approach.

http://jsbin.com/kuwivuxipu/1/edit?html,js,console,output

From that jsbin, the important bits below:

update_facilities = (e)->
    selectedValue = $(e.currentTarget).val()
    continent = data[selectedValue]
    $('#address').text(continent)

$('select').on 'change', update_facilities

html in the head

  <script> 
    // I'm just making up a data structure here
    // and obviously scoping this poorly, but you get the idea
    window.data = {
     1: 'Africa', 
     2: 'Europe', 
     3: 'Africa', 
     7: 'Europe'
    }
  </script>

You can just render this data into your view by doing something like @facilities.to_json and providing a proper serializer (advisable) to create the data you need, or potentially by creating a facilities helper (maybe easier) that builds the data for you:

def facilities_json
  # could also use Enumerable#inject instead of each_with_object
  data = @facilities.each_with_object({}) do |item, memo|
    memo[item.id] = memo.address
  end
  data.to_json
end
Jed Schneider
  • 14,085
  • 4
  • 35
  • 46
  • Thanks for your input, I'll have to wrap my head around this for a bit. Do you mind if I ask you follow up questions on this? There's quite a few ways to do this. I'm really trying to keep my grouped menu ergo why I use the `grouped_collection_select`. It'd be so much nicer if I could just pass a data attribute from within that. – nulltek Mar 29 '15 at 17:14
  • sure. I don't think you can though. like I said, you'd have to create your own select builder with lower level api tools available in rails, like `content_tag`. – Jed Schneider Mar 30 '15 at 01:59
  • Would you mind presenting an example of a `select` tag that I could use in my form that would allow me to use the data attributes? I figured it should be a `f.select` helper with `options_from_collection_for_select` in there but having a hard time with all of my grouped options making it work. Thanks in advance! :) Cheers! – nulltek Mar 30 '15 at 16:03
  • nope, you won't be able to use it like that. you'll have to do something like `content_tag(:select) do ; content_tag(:option) ; end` and build up those content tags inside a helper. – Jed Schneider Mar 30 '15 at 19:52
  • added an example select tag builder. – Jed Schneider Mar 31 '15 at 16:11
  • Thanks for the example select tag build. I'm working on implementing this and seeing how it goes. It just seems like a lot of work to implement something simple. Or maybe I'm just overthinking it. Guess Rails can't do everything. – nulltek Apr 03 '15 at 00:20
  • yep, that's why I advised the other two options for you :) – Jed Schneider Apr 03 '15 at 14:43
  • Dually noted, I'll look into the other two options. They seem more appropriate. – nulltek Apr 06 '15 at 13:38
0

I had a similar problem. What I did to fix it was go to this site http://coffeescript.org/. And compile my Coffescript to Javascript and then include a script tag in the view page I needed it and place my Complied Coffescript to Javascript in there and it worked.

Additionally there is a Railscast partially on this issue #88 Dynamic Select Menus (revised) - RailsCasts. (and if you don't have an account you can view pro episodes for free on youtube! sorry Ryan Bates)

Zippo9
  • 706
  • 2
  • 5
  • 24
  • Thanks, I'm a Railscast member, also a fan of GoRails. My problem is really not with CoffeeScript to JS compilation but more so trying to figure out how to pass a data attribute in `grouped_collection_select` that my jQuery/Coffee can read. – nulltek Mar 29 '15 at 01:04
  • Oh I think you need a Select tag and then pass in options_from_collection_for_select with the arguments of your Table's option values – Zippo9 Mar 29 '15 at 01:21
  • I think you're right about the select tag. But I'm trying to figure out how to pass all of my arguments into `options_from_collection_for_select`. I'm having syntax errors and also need to figure out how to pass in a data attribute so jQuery will render it. – nulltek Mar 29 '15 at 16:53
-1

as js.erb :

faciltiy_address = { <%= Facility.all.map { |e| "'#{e.id}': '#{e.address}'" }.join(', ') %> }

$('your_select').change(function() { $('your_address_wrapper').html(facility_address[$(this).val()])
)}

EDIT :

as js.coffee

$ ->
  faciltiy_address = { <%= Facility.all.map { |e| "#{e.id}: '#{e.address}'" }.join(', ') %> }
  $('#facility_transfer_from_id').change -> 
    $('#transfer-from-address').html(facility_address[parseInt($(this).val())])

Notes :

-not sure of the css ids

-coffee views will parse erb as https://stackoverflow.com/a/8237332/1801147

-depends on the number of facilities, you would prefer an ajax call

Community
  • 1
  • 1
knotito
  • 1,382
  • 1
  • 12
  • 18
  • Thanks for the answer, I'm not sure where I would drop this in. My form is `_form.html.erb` would this need to be another file? Or is there anyway this can be rewritten to be included in a coffeescript or .js file in assets? – nulltek Jan 10 '15 at 14:47
  • you can drop this either in inline js at the end of your form or in e separate forms.js.erb to include in your application.js – knotito Jan 11 '15 at 22:59
  • Gotcha, I'll try it and see if it works. I'm still a bit unsure as to how this works but I'll give it a shot. Thanks! – nulltek Jan 12 '15 at 14:46
  • I tried creating a `_form.js.erb` and pasting the code you supplied. My element to change is `call_transfer_from_id` and I created an empty div with an ID of `transfer_address`. When I go to change the Facility name, nothing happens and there's no output in the console in chrome when I inspect the element. Any thoughts on this? – nulltek Jan 12 '15 at 15:15
  • you have to bind the change event to the right element use jquery css's $('#model-name_attribute') with rails forms conventions – knotito Jan 13 '15 at 16:04
  • Thanks for editing the answer. I'm trying to get this working in my js.coffee file and I'm getting `Unexpected 'COMPARE'` as an exception on the following line: ` faciltiy_address = { <%= Facility.all.map { |e| "#{e.id}: '#{e.address}'" }.join(', ') %> }` Any idea why? – nulltek Mar 30 '15 at 16:23