While working on OnCompare, over the last month, one of the controls we wanted to support was a slider, that let you choose between a range of values, as you can see in the example at right. jQuery-UI provides a slider control and Ruby on Rails already has jQuery built in (we’re using Rails 3). The challenge was that all of our controls are rendered from Formtastic.
Fortunately, Formtastic allows you to add new control types by extending Formtastic::SemanticFormBuilder
. To do that I changed config/initializers/formtastic.rb
to specify our own builder:
Formtastic::SemanticFormHelper.builder = Formtastic::OnCompareFormBuilder
Then I just needed to add a method that responds to :slider type.
class OnCompareFormBuilder < Formtastic::SemanticFormBuilder def slider_input(method, options = {}) collection = find_collection_for_column(method, options) html_options = strip_formtastic_options(options).merge(options.delete(:input_html) || {}) input_id = generate_html_id(method,'') slider_id = "#{input_id}_slider" label_id = "#{input_id}_label" value_items = [] label_items = [] collection.each do |c| label_items << (c.is_a?(Array) ? c.first : c) value = c.is_a?(Array) ? c.last : c value_items << value end label_options = options_for_label(options).merge(:input_name => input_id) label_options[:for] ||= html_options[:id] script_content = "$(function() { var option_values = [#{value_items.map {|v|"'#{v.to_s.gsub(/[']/, '\\\\\'')}'"}.join(',')}] var option_labels = [#{label_items.map {|l|"'#{l.to_s.gsub(/[']/, '\\\\\'')}'"}.join(',')}] $( '##{slider_id}' ).slider({ value:100, min: 0, max: #{collection.count - 1}, step: 1, slide: function( event, ui ) { $( '##{input_id}' ).val( option_values[ui.value] ); $( '##{label_id}' ).text( option_labels[ui.value] ); } }); for(var i = 0; i < option_values.length; i++) { if(option_values[i] == $( '##{input_id}' ).val()) $( '##{slider_id}' ).slider( 'value', i ) } $( '##{label_id}' ).text( option_labels[$( '##{slider_id}' ).slider( 'value' )] ); });" label(method, label_options) << template.content_tag(:script, Formtastic::Util.html_safe(script_content), :type => 'text/javascript') << template.content_tag(:div, template.content_tag(:div, label_items[label_items.count - 1], :class => 'right-label') << template.content_tag(:div, label_items[0], :class => 'left-label') << template.content_tag(:div, nil, html_options.merge(:id => slider_id)), :class => 'slider-holder') << hidden_input(method, options.delete(:as) ) << template.content_tag(:span, nil, :id => label_id) end end
Finally, I generate a ruby call in the ERB that adds a slider using something like this:
<%= f.input :value, :as => :slider, :label => question.text, :hint => question.description %>
This is just what I needed. Thanks!
But where do I put the OnCompareFormBuilder class in the Rails app?
I tried lib/formtastic/on_compare_form_builder.rb but that didn’t seem to work. 😦
Guy –
Just add it to the /lib folder and it should work. Mine is at /lib/on_compare_form_builder.rb.
could you provide a sample for rails 3.1.1 and formtastic (2.0.2)
seems something has been changed and code above doesn’t work for now
i tried
#config/appliction.rb
config.autoload_paths += %W(#{Rails.root}/lib)
#lib/on_compare_form_builder.rb
your code
#config/initializers/formtastic.rb
Formtastic::Helpers::FormHelper.builder = OnCompareFormBuilder
and i got
Formtastic::UnknownInputError
at my view
It looks like the interface for customizing Formtastic has changed significantly in the last ten months. Reading the README from the HEAD (https://github.com/justinfrench/formtastic), it looks like you need to put the slider_input.rb file in app/inputs rather than /lib or /lib/formtastic.
Note also that the new class should be SliderInput and respond to the to_html method, rather than subclassing SemanticFormBuilder.
I haven’t updated this for Formtastic 2.0.2 but hopefully that’s enough to point you in the right direction.