I'm generating a list of checkboxes for a single collection like so:
= f.input :parts, as:check_boxes, collection: @parts_list
I want some checkboxes in the collection to disappear/reappear depending on the value of a select widget higher up in the form. (e.g. choosing "Tracker Robot" from the Robot select means that the "Legs" part checkbox disappears and the "Wheels" checkbox appears, etc.)
What I'd like to do is attach a computed data attribute to each individual Part checkbox, with the attribute value listing the Robots that can use that Part; then some JS will do the work of hiding/showing the checkboxes. However, I don't know how I can generate those data attributes using simple_form.
I would normally create a custom "parts" input, but there seems to be a problem with making custom collection inputs; it looks for a named method (collection_parts) inside form_builder.rb, which won't exist, and if I try and extend the FormBuilder it sends me down a major rabbit hole.
I could write some JS to load the data attrs into the generated HTML, but then I have to generate custom JS based on my Rails data, and that feels like the wrong way to do it.
Let's assume that the form is for Order
model and you are changing the parts
collection based on the value of a field called region
.
Update the form view. Specify the id for form, region
field and parts
field.
= simple_form_for(@order, :html => { :id => "order-form"}) do |f|
= f.input :region, :wrapper_html => { :id => "order-form-region", |
"data-parts-url" => parts_orders_path(:id => @order.id, :region => @order.region)} |
= f.input :parts, as: check_boxes, collection: @parts_list, |
:wrapper_html => { id' => 'parts-check-box-list'} |
Add a new action called parts
in the route.rb
file.
resources :orders do
collection do
get :parts
end
end
Add the new action to your controller
class OrdersController < ApplicationController
# expects id and region as parameters
def parts
@order = params[:id].present? ? Order.find(params[:id]) : Order.new
@parts_list = Part.where(:region => params[:region])
end
end
Add a helper
def parts_collection(order, parts_list)
"".tap do |pc|
# to generate the markup for collection we need a dummy form
simple_form_for(order) do |f|
pc << f.input(:parts, as: check_boxes, collection: parts_list,
:wrapper_html => {:id => 'parts-check-box-list'})
end
end
end
Add a js view for the action (orders/parts.js.erb
)
$('#parts-check-box-list').replaceWith('<%= j(parts_collection(@order, @parts_list)) %>');
Register data change event handlers for region
field in your application.js
$(document).ready(function() {
$('#order-form').on("change", "#order-form-region", function () {
// Access the data-parts-url set in the region field to submit JS request
$.getScript($(this).attr('data-parts-url'));
});
});