# state.rb
class State < ApplicationRecord
belongs_to :country
def self.select_values(country)
return [] unless country
country.states.pluck(:name, :id)
end
end
# country.rb
class Country < ApplicationRecord
has_many :states
def self.select_values
Country.all.map { |country| [country.name, country.id, { data: { url: data_url(country) }}]}
end
private
def self.data_url(country)
Rails.application.routes.url_helpers.country_states_path(country, format: :json)
end
end
# routes.rb
Rails.application.routes.draw do
root to: 'users#index'
resources :users
resources :country, only: [] do
resources :states, only: :index
end
end
# _form.html.erb
<%= form_with(model: user, local: true) do |form| %>
...
<div class="field">
<%= form.label :country_id %>
<%= form.select :country_id,
options_for_select(Country.select_values, user&.country&.id),
{ include_blank: true },
{ class: 'remote-select', data: { target: '#user_state_id' }} %>
</div>
<div class="field">
<%= form.label :state_id %>
<%= form.select :state_id,
options_for_select(State.select_values(user.country), user&.state&.id),
{},
{} %>
</div>
<div class="actions">
<%= form.submit class: 'btn btn-primary'%>
</div>
<% end %>
# states_controller.rb
class StatesController < ApplicationController
def index
country = Country.includes(:states).find(params[:country_id])
render json: country.states.select(:name, :id).map { |state| { id: state.id, name: state.name }}
end
end
# application.js
$(function () {
$(document).on('change', '.remote-select', function (e) {
var target = $(this).attr('data-target')
var url = $(this).find(":selected").attr('data-url')
if (url){
$.ajax({
url: url,
method: 'GET',
success: function (json) {
var target_select = $(target)
target_select.empty()
json.map(function (item) {
value = $('<option></option>').attr('value', item.id).text(item.name)
target_select.append(value)
})
target_select.trigger("chosen:updated")
},
error: function () { }
})
}
})
})