Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a value-dependent data attribute to a simple_form checkbox collection

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.

like image 593
yoz Avatar asked Feb 08 '13 20:02

yoz


3 Answers

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'));
  });
});
like image 121
Harish Shetty Avatar answered Nov 01 '22 01:11

Harish Shetty


I think you can do it like this:

= f.input :parts do
  = f.collection_check_boxes :parts, @parts_list, :id, :to_s, item_wrapper_tag: :label, item_wrapper_class: :checkbox do |b|
    - b.check_box(data: { YOUR DATA ATTRIBUTES HERE }) + b.text
like image 7
Shuo Chen Avatar answered Nov 01 '22 00:11

Shuo Chen


this may be simpler.

Assumptions

@robots - an array containing the list of robots
@parts - a hash containing a list of parts for each robot

Sample Code

# controller
@robots = %w[tracker nontracker]
@parts = { tracker: %w[wheels lcd resistor], nontracker: %w[lcd resistor] }

# view
= f.input :robots, as: :select, collection: @robots, input_html: { id: 'robot-select' }

#parts-list

:javascript
  var parts = #{@parts.to_json};

  $(document).ready(function() {
    $('#robot-select').change(function() {
      $('#parts-list').html('');

      $(parts[$(this).val()]).each(function(index, text) {
        $('#parts-list').append('<input type="checkbox" value=' + text + '>' + text + '</input>')
      })          
    })
  })

you can see this working if you clone https://github.com/jvnill/simple_form_search_app and go to /robots

like image 1
jvnill Avatar answered Nov 01 '22 01:11

jvnill