Skip to content

Instantly share code, notes, and snippets.

@allard
Forked from jamiepenney/bootstrap.rb
Created May 13, 2012 01:04
Show Gist options
  • Select an option

  • Save allard/2670024 to your computer and use it in GitHub Desktop.

Select an option

Save allard/2670024 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 => 'Tags', :class => 'span3'
# .form-actions
# = f.submit 'Save', :class => 'btn btn-primary'
# = link_to 'Cancel', calendar_entries_path, :class => 'btn'
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)
object = @template.instance_variable_get("@#{@object_name}")
return options[:id] || object.class.name.underscore + '_' + field.to_s
end
def jquery_datetime_select(field, options = {})
id = get_object_id(field)
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 = {})
print options
placeholder_text = options[:placeholder_text] || ''
id = get_object_id(field)
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
label = label(field, options[:label])
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}'>" +
label +
"<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}
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|
options = args.last.is_a?(Hash) ? args.last : {}
object = @template.instance_variable_get("@#{@object_name}")
label = label(field, options[:label])
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}'>" +
label +
"<div class='controls'>" +
super(field, *args) +
errorSpan +
"</div>" +
"</div>"
).html_safe
end
end
multipart_helpers.each do |name|
define_method(name) do |field, *args|
options = args.last.is_a?(Hash) ? args.last : {}
object = @template.instance_variable_get("@#{@object_name}")
label = label(field, options[:label])
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}'>" +
label +
"<div class='controls'>" +
super(field, *args) +
errorSpan +
"</div>" +
"</div>"
).html_safe
end
end
end
end
@mattslay
Copy link

I like your change to "set default check_box label if none is set", However, I wish you would make it convert any underscore characters in the field name to a space.

I have fields named like "receive_letter", and the label is rendered as "Receive_letter" and I'd rather see "Receive letter" (note that underscore is converted to a space. It looks better.

Here is the code change I made (All I had to add was a call to gsub() at the end):

label_text = options[:label] || labelOptions[:text] || field.to_s.capitalize.gsub('_', ' ')

@allard
Copy link
Author

allard commented Jul 28, 2012 via email

@mattslay
Copy link

mattslay commented Jul 28, 2012 via email

@allard
Copy link
Author

allard commented Jul 28, 2012 via email

@mattslay
Copy link

mattslay commented Jul 28, 2012 via email

@mattslay
Copy link

allard - I made a fork, and documented my changes at the of the file.

The main thing I accomplished is that it now works with 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

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