Created
February 14, 2012 19:57
-
-
Save jamiepenney/1829710 to your computer and use it in GitHub Desktop.
Form builder for Twitter Bootstrap v2 form elements
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
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}")
It aliases the old methods to super_methodname. I might have got the binding time wrong when I wrote it, this is my first bit of ruby meta programming.
…On 31/07/2012, at 1:27 AM, Matt ***@***.*** wrote:
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}")
```
---
Reply to this email directly or view it on GitHub:
https://gist.github.com/1829710
And apparently we're using super() around the place, so we don't even need
it.
…On 31 July 2012 06:12, Jamie Penney ***@***.*** wrote:
It aliases the old methods to super_methodname. I might have got the
binding time wrong when I wrote it, this is my first bit of ruby meta
programming.
On 31/07/2012, at 1:27 AM, Matt Slay<
***@***.***>
wrote:
> 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}")
> ```
> ---
>
> Reply to this email directly or view it on GitHub:
> https://gist.github.com/1829710
OK. This confirms what I thought. I'm no Ruby expert at all, but it sure seems like this can be removed.
It works just fine without this.
…-----Original Message-----
From: Jamie Penney [mailto:[email protected]]
Sent: Monday, July 30, 2012 3:34 PM
To: Matt Slay
Subject: Re: gist gist: 1829710
And apparently we're using super() around the place, so we don't even need it.
On 31 July 2012 06:12, Jamie Penney ***@***.*** wrote:
It aliases the old methods to super_methodname. I might have got the
binding time wrong when I wrote it, this is my first bit of ruby meta
programming.
On 31/07/2012, at 1:27 AM, Matt Slay<
***@***.***>
wrote:
> 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}")
> ```
> ---
>
> Reply to this email directly or view it on GitHub:
> https://gist.github.com/1829710
---
Reply to this email directly or view it on GitHub:
https://gist.github.com/1829710
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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