4

I have a simple_form input field that is part of a form that I need to update asynchronously when the onchange event is triggered.

The field that I need to update is displayed as a select box since it is a collection. But the collection represents records of a nested set model. So when the user selects a particular value, I want to be able to update the same field with its children and give the user the option of picking any of the children as the value for the same field.

The problem is, how do I update just that field without touching the rest of the form.

Just to give some idea of my current setup: The form looks something like this:

 <%= simple_form_for @product, html: {class: 'form-horizontal'} do |f| %>
     <%= f.input :product_name %>
     <%= f.association :location, collection: @locations, input_html: { data: {remote: :true, url: "/update_locations"} } %>
 <%= f.submit 'Post', class: 'btn btn-default btn-lg' %>
  <% end %>

@product is a new Product which belongs_to Location, @locations is a collection of Location each of which has_many Product hence the use of the simple_form association method Location also acts_as_nested_set

I have this in my routes.rb

get 'update_locations'    => 'products#update_locations'

I need help in completing the controller action:

  def update_locations
    @product = Product.new
    @location = Location.find_by_id(params[:product][:location_id])
    @locations = @location.children
    
    # TODO: render code that updates the simple_form field with
    # the new @locations collection
  end

I am not sure what code should go in the partial view that the controller above is supposed to render.

Steve S
  • 509
  • 1
  • 11
  • 24
  • If you are firing that query with a js event, can you do the rest of your work on the client side and avoid the async call entirely? If you don't need to persist all of those locations quickly (or at all) can you do so once the final selection is made? – jmargolisvt May 04 '15 at 03:17
  • @jmargolisvt I had thought about that but doing all of it client-side but it would require loading a lot more data into the view and writing more code to process the values which would replicate a feature already present server side in the model but I did at last find a way to do it, explained in my answer below. – Steve S May 10 '15 at 14:21

1 Answers1

3

The problem was I didn't know how to update just one element inside a simple_form. I also had a very loose grasp on using AJAX with rails not knowing you could have both HTML and JS partial views for the same controller, as such I was trying to fit all my code in just HTML which would have been cumbersome and going against unobtrusive JavaScript.

Stumbling upon the jQuery method replaceWith was also very helpful.

Armed with this knowledge, I was able to accomplish what I had set out to do.

I created a JavaScript partial view which is rendered in response to the AJAX call. The partial renders a .html.erb partial view which rebuilt a simple_form with the updated element. The JS view then extracts the updated element from the rebuilt form and uses the jQuery replaceWith() method to replace just the specific element inside the initial form while leaving the rest of the form unchanged.

Here is how the JS partial (location.js.erb) looks like:

var updatedLocations = $('#product_location_id', $('<%= j render "updateLocations" %>'))
$('#product_location_id').replaceWith(updatedLocations)

And the HTML erb partial (updateLocations.html.erb) looks something like this:

<%= simple_form_for @product, html: {class: 'form-horizontal'} do |f| %>

<%= f.association :location, collection: @locations, input_html: { data: {update_locations: "true", remote: :true, url: "/update_locations"} } %>

<% end %>

And the controller:

  def update_locations
    @product = Product.new
    @location = Location.find_by_id(params[:product][:location_id])
    @locations = @location.children
    if @locations.empty?
      render nothing: true
    else
      render partial: 'locations'
    end
  end
Steve S
  • 509
  • 1
  • 11
  • 24
  • Sorry for reviving a long-dead post, but what does the `update_locations: "true"` part do in the html input? – Mirror318 Jul 23 '20 at 23:04