Reusing partial with FormBuilder elements across multiple forms and AJAX

154 Views Asked by At

I'm using Carmen and carmen-rails to have an ajax-powered select-a-country, states-list-populates setup. I'm more or less using the code right from their demo, and it works really well.

Except: I want to reuse my "address" partial among several forms: an order form, an update-your-address form, an update-client-address form, that kind of thing. The code is identical in each view.

The example hard-codes the name of the element for state, which won't work for me. If I was rendering this without AJAX, I could simply pass the FormBuilder to the partial, but that's not an option.

Here's my partial code:

<li>
  <label for="country">Country</label>
  <div class="select-wrapper">
    <%= address.country_select :country, {priority: %w(US CA GB), prompt: 'Please select a country'}, class: 'country-select' %>
  </div>
</li>
<li>
  <% if address.object.has_attribute? :address %>
    <%= address.label :address, 'Address' %>
    <%= address.text_field :address %>
  <% else %>
    <%= address.label :street, 'Street' %>
    <%= address.text_field :street %>
  <% end -%>
</li>
<li>
  <%= address.label :city, 'City' %>
  <%= address.text_field :city %>
</li>
<li>
  <label for="province">Province/State</label>
  <div class="select-wrapper">
    <%= render partial: 'shared/subregion_select', locals: { parent_region: address.object.country } %>
  </div>
</li>
<li>
  <%= address.label :zip, 'Postal Code' %>
  <%= address.text_field :zip %>
</li>

This is the partial:

<div id="state_code_wrapper">
  <% parent_region ||= params[:parent_region] %>
  <% country = Carmen::Country.coded(parent_region) %>

  <% if country.nil? %>
    <em>Please select a country.</em>
  <% elsif country.subregions? %>
    <%= subregion_select(:order, :state_code, parent_region) %>
  <% else %>
    <%= text_field(:order, :state_code) %>
  <% end %>
</div>

The problem is the subregion_select(:order, :state, parent_region) -- :order needs to be adjusted to support whatever the FormBuilder would create. Ideally, I'd like it to be f.subregion_select, but I don't know what to pass for f via AJAX.

1

There are 1 best solutions below

0
On

This isn't a true solution to the problem, but rather a workaround (and, arguably, the "correct" way to do it). I've changed the ajax call to fetch the subregions as JSON, and clear and rebuild the items in the select tag using jQuery.

Here are the highlights. It's not the cleanest code, and there are caveats:

  • It doesn't handle a situation with multiple addresses on it
  • It also doesn't display a text field if there are no items to select

For my use case, that's alright, but you may need to do something else.

subregion_options.json.jbuilder:

json.array! @country.subregions do |region|
  json.name region.name
  json.code region.code
end

addresses.js.coffee:

$ ->
  $('select.country-select').change (event) ->
    $country_select = $(this)
    $country_select.attr 'disabled', true

    $region_select = $('select.region-select').first()

    country_code = $(this).val()
    url = "/lookup/subregion_options.json?parent_region=#{country_code}"

    $.getJSON url, (data) ->
      $region_select.find('option').remove()
      $.each data, (index, value) ->
        $region_select.append '<option value=' + value.code + '>' + value.name + '</option>'
      $country_select.attr 'disabled', false