In a Rails 3.2 app I have a pair of dynamic select fields. The typical example of this is a country select which, on change, will filter the options of a state select. This is working perfectly.
But what if I need to allow users to add a new state to the list?
This seems like it should be a fairly common case, but I've been unable to find much information on best approaches. That which I have found relates to select
tags, and the grouped_collection_select
tag that I am using is more challenging.
I would like to know others' opinions and experience implementing something similar. At a very basic level, how would you tackle this?
For example, I could include an Add New option in the select (note, the following indicate concepts rather than valid code), either via the controller or javascript.
@state_options = State.all.collect {|so| [ so.country.id, so.id, so.name ] }
@state_options << [nil, nil, "Add New"]
And then detect whether "Add New" has been selected:
- Either on save, at which point a popup is displayed for the user to fill in the state name, parent country, and other details.
- Or on change, at which point some additional fields are displayed with javascript (better UX, but needs a fallback if javascript not present).
Or I could include an "Add New" link which:
- Displays a popup with new State form which, on save, does an Ajax refresh on the state select field.
- Replaces the State select with some text fields which get built into a new State when the parent form is saved.
So I'd like to know which approach is better, and why?
And I'd also be grateful for some coded examples. I've experimented with the merged @state_options
array above, but am struggling with the syntax - syntax error, unexpected '['
, and my javascript skills are not very advanced.
My code currently looks like this:
<%= simple_form_for(@address ) do |f| %>
<%= f.collection_select :country_id, Country.order(:name), :id, :name, include_blank: true %>
<%= f.grouped_collection_select :region_id, Country.order(:name), :regions, :name, :id, :name, include_blank: true %>
<% end %>
(I'd replace :regions with @state_options in the grouped selection above, if I go that route.)
The coffeescript looks like this
jQuery ->
states = $('#address_state_id').html()
console.log(states)
$('#address_country_id').change ->
country = $('#address_country_id :selected').text()
escaped_country = country.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1')
options = $(states).filter("optgroup[label=#{escaped_country}]").html()
console.log(options)
if options
$('#address_state_id').html(options)
else
$('#address_state_id').empty()