Skip to content

Instantly share code, notes, and snippets.

@jamiepenney
Created February 14, 2012 19:57
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save jamiepenney/1829710 to your computer and use it in GitHub Desktop.
Save jamiepenney/1829710 to your computer and use it in GitHub Desktop.
Form builder for Twitter Bootstrap v2 form elements
# This file goes in config/initializers
require 'bootstrap_form_builder'
# Make this the default Form Builder. You can delete this if you don't want form_for to use
# the bootstrap form builder by default
ActionView::Base.default_form_builder = BootstrapFormBuilder::FormBuilder
# Add in our FormHelper methods, so you can use bootstrap_form_for.
ActionView::Base.send :include, BootstrapFormBuilder::FormHelper
### Only use one of these error handling methods ###
# Get rid of the rails error handling completely.
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance_tag|
"#{html_tag}".html_safe
end
# Only remove the default rails error handling on input and label
# Relies on the Nokogiri gem.
# Credit to https://github.com/ripuk
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
html = %(<div class="field_with_errors">#{html_tag}</div>).html_safe
elements = Nokogiri::HTML::DocumentFragment.parse(html_tag).css "label, input"
elements.each do |e|
if e.node_name.eql? 'label'
html = %(#{e}).html_safe
elsif e.node_name.eql? 'input'
html = %(#{e}).html_safe
end
end
html
end
# This file goes in lib/
# Usage:
#= bootstrap_form_for @calendar_entry do |f|
# %fieldset
# %legend= locals[:title] || 'Edit Calendar Entry'
# = f.text_field :name, :class => 'span3'
# = f.text_area :description, :class => 'span3'
# = f.jquery_datetime_select :start_time, :class => 'span3'
# = f.jquery_datetime_select :end_time, :class => 'span3'
# = f.check_box :all_day
# = f.text_field :tag_string, :label => {:text => 'Tags'}, :class => 'span3'
# .form-actions
# = f.submit 'Save', :class => 'btn btn-primary'
# = link_to 'Cancel', calendar_entries_path, :class => 'btn'
#
# If you don't use HAML, here is the same thing in ERB
# <%= bootstrap_form_for @calendar_entry do |f| %>
# <%= content_tag :legend, (locals[:title] || 'Edit Calendar Entry') %>
# <%= f.text_field :name, :class => 'span3' %>
# <%= f.text_area :description, :class => 'span3' %>
# <%= f.jquery_datetime_select :start_time, :class => 'span3' %>
# <%= f.jquery_datetime_select :end_time, :class => 'span3' %>
# <%= f.check_box :all_day %>
# <%= f.text_field :tag_string, :label => {:text => 'Tags'}, :class => 'span3' do %>
# <p class="help-block">Use commas to separate tags.</p>
# <% end %>
# <div class="form-actions">
# <%= f.submit 'Save', :class => 'btn btn-primary' %>
# <%= link_to 'Cancel', calendar_entries_path, :class => 'btn' %>
# </div>
module BootstrapFormBuilder
module FormHelper
[:form_for, :fields_for].each do |method|
module_eval do
define_method "bootstrap_#{method}" do |record, *args, &block|
# add the TwitterBootstrap builder to the options
options = args.extract_options!
options[:builder] = BootstrapFormBuilder::FormBuilder
if method == :form_for
options[:html] ||= {}
options[:html][:class] ||= 'form-horizontal'
end
# call the original method with our overridden options
send method, record, *(args << options), &block
end
end
end
end
class FormBuilder < ActionView::Helpers::FormBuilder
include FormHelper
def get_error_text(object, field, options)
if object.nil? || options[:hide_errors]
""
else
errors = object.errors[field.to_sym]
if errors.empty? then "" else errors.first end
end
end
def get_object_id(field, options)
object = @template.instance_variable_get("@#{@object_name}")
return options[:id] || object.class.name.underscore + '_' + field.to_s
end
def get_label(field, options)
labelOptions = {:class => 'control-label'}.merge(options[:label] || {})
text = labelOptions[:text] || nil
labelTag = label(field, text, labelOptions)
end
def submit(value, options = {}, *args)
super(value, {:class => "btn btn-primary"}.merge(options), *args)
end
def jquery_date_select(field, options = {})
id = get_object_id(field, options)
date =
if options['start_date']
options['start_date']
elsif object.nil?
Date.now
else
object.send(field.to_sym)
end
date_picker_script = "<script type='text/javascript'>" +
"$( function() { " +
"$('##{id}')" +
".datepicker( $.datepicker.regional[ 'en-NZ' ] )" +
".datepicker( 'setDate', new Date('#{date}') ); } );" +
"</script>"
return basic_date_select(field, options.merge(javascript: date_picker_script))
end
def basic_date_select(field, options = {})
placeholder_text = options[:placeholder_text] || ''
id = get_object_id(field, options)
errorText = get_error_text(object, field, options)
wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end
labelTag = get_label(field, options)
date =
if options[:start_date]
options[:start_date]
elsif object.nil?
Date.now.utc
else
object.send(field.to_sym)
end
javascript = options[:javascript] ||
"
<script>
$(function() {
var el = $('##{id}');
var currentValue = el.val();
if(currentValue.trim() == '') return;
el.val(new Date(currentValue).toString('dd MMM, yyyy'));
});
</script>"
("<div class='#{wrapperClass}'>" +
labelTag +
"<div class='controls'>" +
super_text_field(field, {
:id => id, :placeholder => placeholder_text, :value => date.to_s,
:class => options[:class]
}.merge(options[:text_field] || {})) +
errorSpan +
javascript +
"</div>" +
"</div>").html_safe
end
def jquery_datetime_select(field, options = {})
id = get_object_id(field, options)
date_time =
if options['start_time']
options['start_time']
elsif object.nil?
DateTime.now.utc
else
object.send(field.to_sym)
end
datetime_picker_script = "<script type='text/javascript'>" +
"$( function() { " +
"$('##{id}')" +
".datetimepicker( $.datepicker.regional[ 'en-NZ' ] )" +
".datetimepicker( 'setDate', new Date('#{date_time}') ); } );" +
"</script>"
return basic_datetime_select(field, options.merge(javascript: datetime_picker_script))
end
def basic_datetime_select(field, options = {})
placeholder_text = options[:placeholder_text] || ''
id = get_object_id(field, options)
errorText = get_error_text(object, field, options)
wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end
labelTag = get_label(field, options)
date_time =
if options[:start_time]
options[:start_time]
elsif object.nil?
DateTime.now.utc
else
object.send(field.to_sym)
end
javascript = options[:javascript] ||
"
<script>
$(function() {
var el = $('##{id}');
var currentValue = el.val();
if(currentValue.trim() == '') return;
el.val(new Date(currentValue).toString('dd MMM, yyyy HH:mm'));
});
</script>"
("<div class='#{wrapperClass}'>" +
labelTag +
"<div class='controls'>" +
super_text_field(field, {
:id => id, :placeholder => placeholder_text, :value => date_time.to_s,
:class => options[:class]
}.merge(options[:text_field] || {})) +
errorSpan +
javascript +
"</div>" +
"</div>").html_safe
end
basic_helpers = %w{text_field text_area select email_field password_field check_box number_field}
multipart_helpers = %w{date_select datetime_select}
basic_helpers.each do |name|
# First alias old method
class_eval("alias super_#{name.to_s} #{name}")
define_method(name) do |field, *args, &help_block|
options = args.last.is_a?(Hash) ? args.last : {}
object = @template.instance_variable_get("@#{@object_name}")
labelTag = get_label(field, options)
errorText = get_error_text(object, field, options)
wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end
("<div class='#{wrapperClass}'>" +
labelTag +
"<div class='controls'>" +
super(field, *args) +
errorSpan +
(help_block ? @template.capture(&help_block) : "") +
"</div>" +
"</div>"
).html_safe
end
end
multipart_helpers.each do |name|
define_method(name) do |field, *args, &help_block|
options = args.last.is_a?(Hash) ? args.last : {}
object = @template.instance_variable_get("@#{@object_name}")
labelTag = get_label(field, options)
options[:class] = 'inline ' + options[:class] if options[:class]
errorText = get_error_text(object, field, options)
wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end
("<div class='#{wrapperClass}'>" +
labelTag +
"<div class='controls'>" +
super(field, *args) +
errorSpan +
(help_block ? @template.capture(&help_block) : "") +
"</div>" +
"</div>"
).html_safe
end
end
end
end
@mattslay
Copy link

mattslay commented May 7, 2012 via email

@jamiepenney
Copy link
Author

jamiepenney commented May 7, 2012 via email

@mattslay
Copy link

mattslay commented May 7, 2012 via email

@jamiepenney
Copy link
Author

jamiepenney commented May 7, 2012 via email

@mattslay
Copy link

mattslay commented May 7, 2012 via email

@jamiepenney
Copy link
Author

jamiepenney commented May 7, 2012 via email

@mattslay
Copy link

mattslay commented May 9, 2012

Ok.... It works fine to add number_field to:

basic_helpers = %w{text_field text_area select email_field password_field check_box number_field}

Here is a screenshot of what the number_field looks like in Safari (notice that it has a spinner):

http://www.screencast.com/t/YwmmcYYhV6z

And, yes, even when the database field is an integer field, you can render it as a text_field, and the record will be saved just fine (at least it did on SqlLite). But if you wan't to flag it as a number_field, you can render it that way, in case you want/need the slightly different output, or to take advantage of any special browser, jquery, or javasript handling for type=number that may be out there.

@jamiepenney
Copy link
Author

Thanks a lot Matt, I've added it to the code now.

@allard
Copy link

allard commented May 10, 2012

Here's my updated version with the following changes:

  • Added the option of using either :label_text => 'something' or :label => { :text => 'something' }
  • fixed check_box parsing. The check_box option is the first argument, not the last is with the other helpers
  • Updated the check_box label in according with bootstrap documentation to do <label><input type="checkbox">label text</label>, this will write the check box label after the check box, and with the label enclosing the input, you can click anywhere on the label or form element to change the checkbox.
  • Added a :description => 'left hand label' if you want to include a left hand label, same as the other forms.
  • Removed the :label, :label_text and :description fields from the options so they won't be included in the fields like <input label_text="something">

I haven't tested inline check boxes, or adding options to check boxes because I almost never do.

Enjoy!

# This file goes in lib/
# Usage:
#= bootstrap_form_for @calendar_entry do |f|
#  %fieldset
#    %legend= locals[:title] || 'Edit Calendar Entry'
#    = f.text_field :name, :class => 'span3'
#    = f.text_area :description, :class => 'span3'
#    = f.jquery_datetime_select :start_time, :class => 'span3'
#    = f.jquery_datetime_select :end_time, :class => 'span3'
#    = f.check_box :all_day, { :label_text => 'Agree?', :description => 'left hand label' }
#    = f.text_field :tag_string, :label => {:text => 'Tags'}, :class => 'span3'
#    .form-actions
#      = f.submit 'Save', :class => 'btn btn-primary'
#      = link_to 'Cancel', calendar_entries_path, :class => 'btn'
#
# If you don't use HAML, here is the same thing in ERB
# <%= bootstrap_form_for @calendar_entry do |f| %>
#   <%= content_tag :legend, (locals[:title] || 'Edit Calendar Entry') %>
#   <%= f.text_field :name, :class => 'span3' %>
#   <%= f.text_area :description, :class => 'span3' %>
#   <%= f.jquery_datetime_select :start_time, :class => 'span3' %>
#   <%= f.jquery_datetime_select :end_time, :class => 'span3' %>
#   <%= f.check_box :all_day, { :label_text => 'Agree?', :description => 'left hand label' } %>
#   <%= f.text_field :tag_string, :label => {:text => 'Tags'}, :class => 'span3' do %>
#     <p class="help-block">Use commas to separate tags.</p>
#   <% end %>
#   <div class="form-actions">
#     <%= f.submit 'Save', :class => 'btn btn-primary' %>
#     <%= link_to 'Cancel', calendar_entries_path, :class => 'btn' %>
#   </div>

module BootstrapFormBuilder
  module FormHelper
    [:form_for, :fields_for].each do |method|
      module_eval do
        define_method "bootstrap_#{method}" do |record, *args, &block|
          # add the TwitterBootstrap builder to the options
          options           = args.extract_options!
          options[:builder] = BootstrapFormBuilder::FormBuilder

          if method == :form_for
            options[:html] ||= {}
            options[:html][:class] ||= 'form-horizontal'
          end

          # call the original method with our overridden options
          send method, record, *(args << options), &block
        end
      end
    end
  end

  class FormBuilder < ActionView::Helpers::FormBuilder
    include FormHelper

    def get_error_text(object, field, options)
      if object.nil? || options[:hide_errors]
        ""
      else
        errors = object.errors[field.to_sym]
        if errors.empty? then "" else errors.first end
      end
    end

    def get_object_id(field, options)
      object = @template.instance_variable_get("@#{@object_name}")
      return options[:id] || object.class.name.underscore + '_' + field.to_s
    end

    def get_label(field, options)
      labelOptions = {:class => 'control-label'}.merge(options[:label] || {})
      text = options[:label_text] || labelOptions[:text] || nil
      options.delete(:label)
      options.delete(:label_text)
      labelTag = label(field, text, labelOptions)
    end

    def submit(value, options = {}, *args)
      super(value, {:class => "btn btn-primary"}.merge(options), *args)
    end

    def jquery_date_select(field, options = {})
      id = get_object_id(field, options)

      date = 
        if options['start_date']
          options['start_date']
        elsif object.nil?
          Date.now
        else
          object.send(field.to_sym)
        end

      date_picker_script = "<script type='text/javascript'>" +
            "$( function() { " +
              "$('##{id}')" +
              ".datepicker( $.datepicker.regional[ 'en-NZ' ] )" +
              ".datepicker( 'setDate', new Date('#{date}') ); } );" +
          "</script>"
      return basic_date_select(field, options.merge(javascript: date_picker_script))
    end

    def basic_date_select(field, options = {})
      placeholder_text = options[:placeholder_text] || ''
      id = get_object_id(field, options)

      errorText = get_error_text(object, field, options)
      wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
      errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end

      labelTag = get_label(field, options)

      date = 
        if options[:start_date]
          options[:start_date]
        elsif object.nil?
          Date.now.utc
        else
          object.send(field.to_sym)
        end

      javascript = options[:javascript] || 
        "
  <script>
    $(function() { 
      var el = $('##{id}');
      var currentValue = el.val();
      if(currentValue.trim() == '') return;
      el.val(new Date(currentValue).toString('dd MMM, yyyy'));
    });
  </script>"

      ("<div class='#{wrapperClass}'>" + 
        labelTag +
        "<div class='controls'>" +
          super_text_field(field, {
            :id => id, :placeholder => placeholder_text, :value => date.to_s,
            :class => options[:class]
          }.merge(options[:text_field] || {})) + 
          errorSpan +
          javascript +
        "</div>" +
      "</div>").html_safe
    end

    def jquery_datetime_select(field, options = {})
      id = get_object_id(field, options)

      date_time = 
        if options['start_time']
          options['start_time']
        elsif object.nil?
          DateTime.now.utc
        else
          object.send(field.to_sym)
        end

      datetime_picker_script = "<script type='text/javascript'>" +
            "$( function() { " +
              "$('##{id}')" +
              ".datetimepicker( $.datepicker.regional[ 'en-NZ' ] )" +
              ".datetimepicker( 'setDate', new Date('#{date_time}') ); } );" +
          "</script>"
      return basic_datetime_select(field, options.merge(javascript: datetime_picker_script))
    end

    def basic_datetime_select(field, options = {})
      placeholder_text = options[:placeholder_text] || ''
      id = get_object_id(field, options)

      errorText = get_error_text(object, field, options)
      wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
      errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end

      labelTag = get_label(field, options)

      date_time = 
        if options[:start_time]
          options[:start_time]
        elsif object.nil?
          DateTime.now.utc
        else
          object.send(field.to_sym)
        end

      javascript = options[:javascript] || 
        "
  <script>
    $(function() { 
      var el = $('##{id}');
      var currentValue = el.val();
      if(currentValue.trim() == '') return;
      el.val(new Date(currentValue).toString('dd MMM, yyyy HH:mm'));
    });
  </script>"

      ("<div class='#{wrapperClass}'>" + 
        labelTag +
        "<div class='controls'>" +
          super_text_field(field, {
            :id => id, :placeholder => placeholder_text, :value => date_time.to_s,
            :class => options[:class]
          }.merge(options[:text_field] || {})) + 
          errorSpan +
          javascript +
        "</div>" +
      "</div>").html_safe
    end

    basic_helpers = %w{text_field text_area select email_field password_field number_field}

    multipart_helpers = %w{date_select datetime_select}

    trailing_label_helpers = %w{check_box}

    basic_helpers.each do |name|
      # First alias old method
      class_eval("alias super_#{name.to_s} #{name}")

      define_method(name) do |field, *args, &help_block|
        options = args.last.is_a?(Hash) ? args.last : {}
        object = @template.instance_variable_get("@#{@object_name}")

        labelTag = get_label(field, options)

        errorText = get_error_text(object, field, options)

        wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
        errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end 
        ("<div class='#{wrapperClass}'>" +
            labelTag + 
            "<div class='controls'>" + 
              super(field, *args) +
              errorSpan +
              (help_block ? @template.capture(&help_block) : "") +
            "</div>" +
          "</div>"
        ).html_safe
      end
    end  

    multipart_helpers.each do |name|
      define_method(name) do |field, *args, &help_block|
        options = args.last.is_a?(Hash) ? args.last : {}
        object = @template.instance_variable_get("@#{@object_name}")

        labelTag = get_label(field, options)

        options[:class] = 'inline ' + options[:class] if options[:class]

        errorText = get_error_text(object, field, options)

        wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
        errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end 
        ("<div class='#{wrapperClass}'>" +
            labelTag + 
            "<div class='controls'>" + 
                super(field, *args) +
                errorSpan +
                (help_block ? @template.capture(&help_block) : "") +
            "</div>" +
          "</div>"
        ).html_safe
      end
    end

    trailing_label_helpers.each do |name|
      # First alias old method
      class_eval("alias super_#{name.to_s} #{name}")

      define_method(name) do |field, *args, &help_block|
        options = args.first.is_a?(Hash) ? args.first : {}
        object = @template.instance_variable_get("@#{@object_name}")

        label_text = options[:label_text] || labelOptions[:text] || ""
        options.delete(:label)
        options.delete(:label_text)

        errorText = get_error_text(object, field, options)

        wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
        errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end 
        description = if options[:description] then %{<label class="control-label">#{options[:description]}</label>} else "" end
        options.delete(:description)
        ("<div class='#{wrapperClass}'>" +
          description +
          "<div class='controls'>" + 
            "<label for='#{field}' class='control-group'>" + 
              super(field, *args) +
              errorSpan +
              (help_block ? @template.capture(&help_block) : "") +
              label_text +
            "</label>" +
            "</div>" +
          "</div>"
        ).html_safe
      end
    end
  end
end

@mattslay
Copy link

@allard - I'm trying out your version, but I'm getting this error on a check_box, where it's complaining about the labelOptions variable.

undefined local variable or method `labelOptions' for #<BootstrapFormBuilder::FormBuilder:0x007fb6a32c9fa0>

It's coming from this line in your code:

label_text = options[:label_text] || labelOptions[:text] || ""

I don't think your code is properly handling the creation of the labelOptions variable. I see no reference to its creation in your code.

In my usage, I am calling it like this in my erb file:

<%= f.check_box :remember_me %>

@jamiepenney
Copy link
Author

jamiepenney commented May 11, 2012 via email

@allard
Copy link

allard commented May 13, 2012

Spot on @mattslay. I just use :label_text when tested with this check box and forgot to include the line to grab the labelOptions. And from request by @jamiepenney I've now forked the gist with my updated version with this thing fixed.

@mattslay
Copy link

I've tried @allard's version, and as far as placing the label inline with the checkbox, it does that just fine. (Although the text is a little too close to the checkbox for my liking, but I'm sure that's a CSS issue)

See this screenshot which compares the original version by @jamiepenny compared to the inline version by @allard:

http://www.screencast.com/t/jOn92rvMFsq

@allard
Copy link

allard commented May 14, 2012

While it could technically be possible to insert an additional space or so to get the padding between the element and the label, I think it's definitely a css issue. I use:

input.checkbox {
  margin: 0 0 2px 0;
}

Also, don't forget that you can also use :description => 'something' if you want to add a left hand label as well with my version.

@allard
Copy link

allard commented Jun 5, 2012

I'm pretty sure my previous css override isn't needed any more. There was some class that was missing or something that's been fixed in the latest gist. I've also added inline help messages to my gist for those interested so you can do:

<%= f.text_field :email, :help => "Please enter a valid email address" %>

and the help message will show up to the right of the input.

@mattslay
Copy link

Is there any reason we couldn't go ahead and set the id attribute on the submit button to 'submit' ? I like having an id attribute set as for each input control, even the Submit button. It is helpful in jquery and general css, plus it's helpful in Capybara tests. Is there a potential conflict of having 2 or more submit buttons on a form? If so, any ideas on how to create unique id's? I know I can pass in an id as a hash option to f.submit(), but 99% of the time I would call it "submit", so I thought it could be done at this lower level?

def submit(value, options = {}, *args)
  super(value, {:class => "btn btn-primary", :id => "submit"}.merge(options), *args)
end

@allard
Copy link

allard commented Jun 13, 2012 via email

@jamiepenney
Copy link
Author

jamiepenney commented Jun 13, 2012 via email

@drew1two
Copy link

Hey again Jamie. Been around the block a few times now and still keep coming back to here :)
Quick question on what you reckon it would take to implement something like negative-captcha with your form builder.? I'm getting better but something like this would / could still be outta my league.
Would be nice if you're builder had it's own captcha rolled into it (prolly a negative or honeypot type so no images or files required, just hidden fields with clever keys for names kinda thing) :).

Cheers again for the work.

@jamiepenney
Copy link
Author

Probably outside of the scope of this code sorry. It would require changes on the controller side, which isn't something I want to add.

You could probably hack a hidden field captcha into it on your own if you want though. Something like this:

module_eval do
    define_method "bootstrap_captcha_form_for" do |record, *args, &block|

      new_block = lambda { |f| 
          f.hidden ("put your hidden field options here") 
          block.call f
      }
      bootstrap_form_for record, *args, &block
    end
end

This was all composed in browser, so I don't even know if it will run. Will give you something to start with at least.

@drew1two
Copy link

drew1two commented Jun 18, 2012 via email

@mattslay
Copy link

I need some help getting this to work with the collection_select() helper:

collection_select(:contact, :church_office_id, ChurchOffice.all(), :id, :name)

Heres a link to a screen shot of the generated form using the regular Rails helper:

http://www.screencast.com/t/ZJdQJcUkwC

But when I added 'collection_select' to the list of basic_helpers, (so it will wrap everything in Bootstrap markup), it gives this error at run time:

undefined method 'merge' for :name:Symbol

I guess there needs to be more code to handle all those extra parameters that collection_select() uses. I looked at the code, but I simply do not know enough about Rails/Ruby at this time to see what is needed to handle this.

Here is a screenshot of the error page:
http://www.screencast.com/t/zf8oUXsg4iz

Ideally, just like we can do with checkboxes, I'd love to be able to pass in a caption for the label that would be generated right above the selection list box.

@allard
Copy link

allard commented Jul 28, 2012 via email

@mattslay
Copy link

Jamie - I've fork allards version, and added support for f.collection_select(). I also refactored the bootstrap html wrapping out to some new handler methods, because it was beginning to have some redundant code now that more controls are being supported.

See my fork here: https://gist.github.com/3206827

@mattslay
Copy link

Can somone explain what this alias stuff does? I don't see that it does anything? I commented it out and everything still seems to run fine.

  # First alias old method
  class_eval("alias super_#{name.to_s} #{name}")

@jamiepenney
Copy link
Author

jamiepenney commented Jul 30, 2012 via email

@jamiepenney
Copy link
Author

jamiepenney commented Jul 30, 2012 via email

@mattslay
Copy link

mattslay commented Jul 30, 2012 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment