Skip to content

Instantly share code, notes, and snippets.

@mattslay mattslay/bootstrap.rb forked from allard/bootstrap.rb
Created Jul 30, 2012

Embed
What would you like to do?
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/
#
# Original gist by Jamie Penny: https://gist.github.com/1829710
# Fork by allard: https://gist.github.com/2670024
# Fork of allard's version by Matt Slay: https://gist.github.com/3204634
#
# Changes:
#----------------------------------------------------------------------------
# 2012-06-05 allard: Added inline help messages, set default check_box label if none is set
#
# 2012-07-29 MattSlay: Added 'collection_select' to the basic_helpers list.
# For default label text on checkboxes, underscore chars are converted to spaces.
# Refactored repetitive code from basic_helpers and multipart_helpers
# into control_opening_html() and control_closing_html().
#----------------------------------------------------------------------------
# Usage (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 => 'Agree?, :description => 'left hand label'' } %>
# <%= f.text_field :tag_string, :label_options => { :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>
# <% end %>
#
# Inline help:
# <%= f.text_field :name, :help => "inline help message"
#
# collection_select example:
# <%= f.collection_select :church_office_id, ChurchOffice.all(), :id, :name %>
#
#
#----------------------------------------------------------------------------
#Usage (HAML):
#
#= 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' do
# %p.help-block
# Use commas to separate tags.</p>
#
# .form-actions
# = f.submit 'Save', :class => 'btn btn-primary'
# = link_to 'Cancel', calendar_entries_path, :class => 'btn'
#
# collection_select example:
# = f.collection_select :church_office_id, ChurchOffice.all(), :id, :name
#------------------------------------------------------------------------------
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
include ActionView::Helpers::TagHelper
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_options] || {})
text = options[:label] || labelOptions[:text] || nil
options.delete(:label)
options.delete(:label_options)
labelTag = label(field, text, labelOptions)
end
def inline_help(options)
return "" unless options[:help]
text = options[:help]
options.delete(:help)
content_tag :span, text, :class => 'help-inline'
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
def control_opening_html
labelTag = get_label(@field, @options)
errorText = get_error_text(@object, @field, @options)
wrapperClass = 'control-group' + (errorText.empty? ? '' : ' error')
#-- Build up the opening html to wrap everything in Twitter control Bootstap markup
("<div class='#{wrapperClass}'>" +
labelTag +
"<div class='controls'>")
end
def control_closing_html(help_block)
errorText = get_error_text(@object, @field, @options)
errorSpan = if errorText.empty? then "" else "<span class='help-inline'>#{errorText}</span>" end
#-- Build up the return string
( inline_help(@options) +
errorSpan +
(help_block ? @template.capture(&help_block) : "") +
"</div>" +
"</div>")
end
basic_helpers = %w{text_field text_area select email_field password_field number_field collection_select}
multipart_helpers = %w{date_select datetime_select}
trailing_label_helpers = %w{check_box}
basic_helpers.each do |name|
define_method(name) do |field, *args, &help_block|
@field = field
@options = args.last.is_a?(Hash) ? args.last : {}
@object = @template.instance_variable_get("@#{@object_name}")
( control_opening_html() +
super(field, *args) +
control_closing_html(help_block)
).html_safe
end
end
multipart_helpers.each do |name|
define_method(name) do |field, *args, &help_block|
@field = field
@options = args.last.is_a?(Hash) ? args.last : {}
@object = @template.instance_variable_get("@#{@object_name}")
@options[:class] = 'inline ' + options[:class] if options[:class]
( control_opening_html() +
super(field, *args) +
control_closing_html()
).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}")
labelOptions = {:class => 'checkbox'}.merge(options[:label_options] || {})
labelOptions["for"] = "#{@object_name}_#{field}"
label_text = options[:label] || labelOptions[:text] || field.to_s.capitalize.gsub('_', ' ')
options.delete(:label)
options.delete(:label_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
description = if options[:description] then %{<label class="control-label">#{options[:description]}</label>} else "" end
options.delete(:description)
("<div class='#{wrapperClass}'>" +
description +
"<div class='controls'>" +
tag(:label, labelOptions, true) +
super(field, *args) +
inline_help(options) +
errorSpan +
(help_block ? @template.capture(&help_block) : "") +
label_text +
"</label>" +
"</div>" +
"</div>"
).html_safe
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.