Adding a custom jQuery-UI control to Formtastic in Ruby on Rails

A jQuery-UI slider rendered with FormtasticWhile 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 %>
About these ads

5 thoughts on “Adding a custom jQuery-UI control to Formtastic in Ruby on Rails

  1. 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. :(

  2. 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

  3. 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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s