5

I have a Rails app where I'm trying to select a list of facilities based on what region is selected in a form. So far, I've implemented group_collection_select to do this as well as a bit of CoffeeScript.

It works when creating a new record and selecting a region. The behavior being to only show facilities listed for the selected region. What does not work is when editing a record, selecting the facilities shows all of the facilities grouped by region instead of constraining the facilities to the selected region.

If I select another region and then select the original reason, the proper list of facilities show up.

I'd like to learn how to refactor the CoffeeScript to where when editing the record the function is fired both on page load (when editing) and on change.

Lastly, there are use cases when the transfer_from_id is set to nil/blank and we use a text field called transfer_from_other. Currently if I do not select a facility and fill in transfer_from_other, because of the CoffeeScript loading the facilities in transfer_from_id it will set the transfer_from_id by the first facility in the scope. I'd like to make this to where if no facility is selected, the transfer_from_id is nil so I can use transfer_from_other.

Here's what my code looks like:

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()

region.rb

has_many :facilities

facility.rb

attr_accessible :region_id
belongs_to :region

_form.html.erb excerpt

      <%= f.label :region %>
      <%= f.collection_select(:region_id, Region.all, :id, :area, {:include_blank => true}, {:class => 'select', required: true}) %>
      <%= f.label :Transfer_From %>
      <%= f.grouped_collection_select :transfer_from_id, Region.order(:area), :active_facilities, :area, :id, :facility_name, {include_blank: true}, class: 'select' %>
      <%= f.label :Transfer_From_Other%>
      <%= f.text_field :transfer_from_other %>

If my question and examples are not clear, please let me know and I'll be happy to edit.

nulltek
  • 3,247
  • 9
  • 44
  • 94

2 Answers2

6

Regarding updating the select on both page load and on selection change, unless I'm missing something isn't it sufficient to simply break out the select update portion into its own function, that you then call once on page load, and then on each selection change?

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

  update_facilities = ->
    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()      

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

  update_facilities()

Regarding the second part, each time you update the #call_transfer_from_id select, you will lose the blank option. This second version adds in and selects a blank option each time you select a region:

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
      $('#call_transfer_from_id').html(options)
      # Add in a blank option at the top
      $('#call_transfer_from_id').prepend("<option value=''></option>")
      # Ensure that the blank option is selected
      $('#call_transfer_from_id option:first').attr("selected", "selected");
    else
      $('#call_transfer_from_id').empty()      

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

  update_facilities()
Stefan Magnuson
  • 870
  • 1
  • 10
  • 10
  • Thanks for chiming in. This works for both the page load and on selection change. But if I leave the transfer_from_id field blank intentionally and use the transfer_from_other to input an address, the Call record is created with transfer_from_id selecting the first facility in the list. How can I set it up to where transfer_from_id remains blank/nil unless I specifically select a facility? We're making progress so far and thanks again for your help. – nulltek Sep 08 '14 at 12:14
  • I have updated the answer. If I understand you correctly, you're saying that when you choose a region and the facilities are updated, the first facility is automatically selected as there is no blank option. Assuming I am understanding the problem correctly then the edited answer should do what you want. If not, I think it would help a lot if you could create a fiddle demonstrating the problem and link to it in your question. – Stefan Magnuson Sep 09 '14 at 06:58
  • Thanks for updating your answer. I tried this out and and ran into a problem when editing a call. When editing a call the line `$('#call_transfer_from_id option:first').attr("selected", "selected");` caused the facility transfer_from_id field to select the blank option. When editing a call the facility needs to stay on record. So I removed that line `$('#call_transfer_from_id option:first').attr("selected", "selected");` and left your code as is and it seems to be working. I'll test further and let you know. Thanks for your efforts! – nulltek Sep 09 '14 at 12:52
  • One thing I was going to ask is why `update_facilities()` is called twice? Maybe I'm not understanding it. Was just curious. – nulltek Sep 09 '14 at 13:03
  • Just an update. I've spent a few minutes testing this and so far it works once I removed the line `$('#call_transfer_from_id option:first').attr("selected", "selected");`. I'm going to test it further, but it looks like it's working as I originally wanted. I've accepted your answer and voted up. I'll comment if I run into any problems. Thanks a bunch :) – nulltek Sep 09 '14 at 13:13
  • Great! Glad to hear it's working for you. Regarding pre-selecting the blank option, you will want to remove that line (as you have done) if that isn't what you want. On `update_facilities()` being called twice, it is present twice because one time it is being called when everything is set up (the page loads), and then again every time you make a region selection. If any of that is unclear please let me know. – Stefan Magnuson Sep 11 '14 at 02:05
  • Thanks for the explanation, it's all clear now. Was just confused about `update_facilities()` being called twice. I am still getting used to CoffeeScript and it's formatting. Just threw me off a little. Thanks again! – nulltek Sep 11 '14 at 11:44
  • Just a heads up. I was testing this in Chrome an it works fine but in Firefox if I edit a call, the first facility is auto-populated in the form even if the transfer_from_id was nil. Any thoughts on this? – nulltek Sep 12 '14 at 21:24
  • Whenever you select a different region, the list of facilities is regenerated from scratch. In my answer I made sure that it would include a blank option, and that the blank option would be selected. Then, when you are loading the edit view, you need to ensure that the facilities drop down also contains a blank option, as otherwise it will default to the first item in the list. So, in all cases is there a blank option available in the facilities drop down? – Stefan Magnuson Sep 14 '14 at 09:58
  • Yes, whether it's on region change or page load there is a a blank option. However when editing a call record and leaving in this line: `$('#call_transfer_from_id option:first').attr("selected", "selected");` it forces the first option (blank) to be selected and does not show the facility that is on the record. This happens in Chrome or Firefox. When I remove that line of code in Chrome it works fine, blank option when creating a call and the facility (if transfer_from_id is assigned) shows up in the form when editing. In firefox when editing it will default to the first facility. – nulltek Sep 14 '14 at 11:58
  • In firefox on edit as I mentioned it defaults to the first facility regardless if `transfer_from_id` is set to an id or is nil. The problem seems to be with Firefox and the way it interprets the JS/CoffeeScript. If you want me to create a gist of all of the code I can do that. I'm just unsure as to why it doesn't work properly in Firefox. Thanks for your help! – nulltek Sep 14 '14 at 12:00
  • It's almost like Firefox ignores the `$('#call_transfer_from_id').prepend("")` line on edit and just selects the first real value of the facilities. I'm not sure why it's doing this. If I knew CoffeeScript/JS better I'd be able to figure it out, but I'm really new to it unfortunately. I appreciate whatever help you can offer, Stefan. – nulltek Sep 14 '14 at 12:08
  • I used the Firefox debugger and inspected the element. Firefox shows the prepended blank option [example](http://cl.ly/image/1D280H3C242o) but still selects the first facility in the list when editing the call record. – nulltek Sep 14 '14 at 14:50
  • I think I may have figured this out. Please review my answer and tell me what you think. – nulltek Sep 14 '14 at 18:49
0

So I've been playing around with this problem and I think I've found a solution that works on both Chrome and Firefox respectively.

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()

I was able to refactor and remove a line of code and set the options and the blank option in one line instead of using prepend. For some reason Firefox didn't like the prepend method. So by calling this line: $('#call_transfer_from_id').html("<option value=''></option>" + options) I am able to pass the options and insert a blank option at the beginning of the array.

So far I've tested on both Chrome and Firefox and it displays the following behavior.

When creating a new call, facilities are constrained by region, and a blank option is presented in the field for selection. If you enter a transfer_from_other address and leave transfer_from_id nil it retains the nil value.

When editing a call that has transfer_from_other address filled out the transfer_from_id no longer populates with the first facility in the list, instead it lists the blank (nil) option.

When editing a call that has transfer_from_id assigned, it retains the original value of the record and still constrains by region.

Can you look at my code and see if it makes sense. I stumbled upon doing it this way and somehow got it to work. Trying to understand why this works on both browsers versus your answer which only works on Chrome.

nulltek
  • 3,247
  • 9
  • 44
  • 94
  • I tested the code in Safari, so it seems safe to say that it works in Safari and Chrome, but that Firefox had issues. If the only change here is to replace the one line that called `prepend` then I would suggest making that edit to my original answer. – Stefan Magnuson Sep 15 '14 at 05:15
  • I edited your original answer to reflect the change I made. Now the question is why did `prepend` not work in Firefox? I figured it would work fine. Could it be perhaps the version of jQuery I'm running `jquery-rails 2.1.4` gem? – nulltek Sep 15 '14 at 11:41