Dieglock
Dieglock

Reputation: 169

Custom country/state dropdowns with rails

I been diggins some days on gems for country and state/province selection. Some are great (like country-state-select) but not for my needs.

The almost mandatory country_select gem is good but lacks states. Nevertheless is based on this very cool gem called countries. Gem countries really puts together a lot of good info, lets say Mozambique and its subdivisions, and so on, for over 300 countries, and includes i18n.

Having countries gem in the app, all is needed is some javascript to interact with it. Calls are made with this:

country = ISO3166::Country.new('US')

Here is the form:

<%= simple_form_for(@order) do |f| %>
<div><%= f.input :country %></div>
<div><%= f.input :state %></div>
<%= f.button :submit, t('.submit') %>
<% end %>

<script type="text/javascript">
states_drop_down = $("#order_state");
$("#order_country").change(function() {

// How to make this work?
    <% unless params[:country].nil? %>
        states_drop_down.clearAttributes();
        <% ISO3166::Country.new(params[:country]).states.each do |state| %>
            states_drop_down.setAttribute(<%= state[:name] %>, <%= state[:alpha2] %>); // How to log to check if new attributes are present?
        <% end %>
        states_drop_down.reload(); // How to reload the state simple_form input?
    <% end %>
});

The goal is the known one, to populate state selector with correct country every time the country dropdown changes. Any help? Thanks.

Upvotes: 1

Views: 1451

Answers (1)

Dieglock
Dieglock

Reputation: 169

I found a solution, even though is not using gem countries anymore. Data is seeded to database and pulled from there. Found the data here.

Then all is needed is few steps:

// 1. new action at controller, in my case was orders. This receives the calls and renders the views.
  def update_states
      country = Country.find(params[:nation_id])
      @states = country.states
      respond_to do |format|
        format.js
      end   
  end

// 2. new get at routes to find the new action
  get "orders/update_states", as: "update_states"

// 3. simple_form with collections. Note uses nation to avoid the simple_form country selector error.
  <%= simple_form_for(@order) do |f| %>
  <div><%= f.input :nation, collection: @countries %></div>
  <div><%= f.input :state, collection: @states %></div>
  <%= f.button :submit, t('.submit') %>
  <% end %>

// 4. new doc at app/views/states/_state.html.erb to render inside the dropdown selector.
  <option value="<%= state.id %>"><%= state.name %></option>

// 5. new lines at app/assets/javascripts/orders.js.coffee to listen to nation or country selector.
  $ ->
  $(document).on 'change', '#order_nation', (evt) ->
  $.ajax 'update_states',
  type: 'GET'
  dataType: 'script'
  data: {
    nation_id: $("#order_nation option:selected").val()
  }
  error: (jqXHR, textStatus, errorThrown) ->
    console.log("AJAX Error: #{textStatus}")
  success: (data, textStatus, jqXHR) ->
    console.log("State selection is working!")

// 6. and app/views/orders/update_cities.js.cofee, the piece that close the circle. This actually renders the info and views.
  $("#order_state").empty()
  .append("<%= escape_javascript(render(:partial => @states)) %>")

Must thanks Kernel Garden, I found the javascript I was looking here.

Upvotes: 1

Related Questions