Cascading selects with AJAX in Rails
I bet you had an issue with dynamic populating of values for select which depends on a chosen value of other select. For example when I choose the country, select of the state should be populated by the states of the chosen country. There are many solutions of this problem. You could find them with Google but none of them is suitable for me because of bugs or using the approach with caching. I have had this issue again and again and my googling was not successful. Eventually some time ago I invented jQuery library and approach with solves this problem and completely suits me. This approach assumes that you don’t use javascript frameworks like Ember.js or Angular.js. In other case, my solution may be needless in your application.
Problem
This image will clarify the problem without any words:
Solution
The first thing which I should pay attention to in my solution is a jQuery library:
app/assets/javascripts/jquery-dynamic-selectable.coffee
This peace of code extends jQuery with a dynamicSelectable function which you can call on select which should be listened to change and populate the dependent select. The listened select should has data attributes: selectable-url and selectable-target. Their names explain what they are for:
selectable-url - is an url pattern with the model id. For example: /dynamic_select/:country_id/states
. In this case the pattern should be populated with the chosen country id (assuming that we add listener to country’s select) and request will go to this url to get JSON data for populating related select.
selectable-target - is a css selector of select which should be populated with given JSON data from the server.
I call dynamicSelectable function for every select on the page which has both data attributes selectable-url and selectable-target simultaneously:
app/assets/javascripts/application.coffee
To make our application workable we should have a controller which will be responsible for the route /dynamic_select/:country_id/states
. Firstly have a look at how I generate route:
config/routes.rb
And this is how my controller looks:
app/controllers/dynamic_select/states_controller.rb
And this is a template of index action:
app/views/dynamic_select/states/index.json.jbuilder
Returned JSON data from server should not include root elements:
config/intializers/wrap_parameters.rb
And the last thing which I should focus on is a form:
Check out the code on github and the live demo here.
If you have any proposals concerning this solution or if you have spotted any typo, bug, inconsistency or lacks feel free to contact with me.