Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A Simple Custom Form Builder Helper for Twitter Bootstrap 3. The main part of this code is taken from https://coderwall.com/p/x63_qg
# app/helpers/forms_helper.rb
module FormsHelper
# A custom FormBuilder class for Rails forms with Twitter Bootstrap 3
class CustomFormBuilder < ActionView::Helpers::FormBuilder
# ActionView::Helpers::FormHelper and ActionView::Helpers::FormBuilder
# methods each have different args. This hash stores the names of the args
# and the methods that have those args in order to DRY up the method aliases
# defined below.
METHOD_NAMES_FOR_ARG_SETS = {
'method,options={}' => %w{
email_field file_field
hidden_field number_field password_field phone_field range_field
search_field telephone_field text_area text_field url_field
},
# Removing checkboxes for now - the error field causes alignment issues
# "method,options={},checked_val='1',unchecked_val='0'" => %w{ check_box },
# "method,collection,value_method,text_method,options={},html_options={},&block" => %w{
# collection_check_boxes collection_radio_buttons
# },
"method,collection,value_method,text_method,options={},html_options={}" => %w{
collection_select
},
"method,options={},html_options={}" => %w{date_select datetime_select time_select},
"method,tag_value,options={}" => %w{ radio_button },
"method,choices,options={},html_options={}" => %w{ select }
}
for args_set, method_names in METHOD_NAMES_FOR_ARG_SETS
html_class_in = args_set.index("html_options") ? 'html_options' : 'options'
args_without_defaults = args_set.gsub(/\=[^,]+/, '')
for method_name in method_names
method_str = <<-RUBY
def #{method_name}_with_error_field(#{args_set})
update_options_with_form_control_class!(#{html_class_in})
content = #{method_name}_without_error_field(#{args_without_defaults})
append_error_field_to_content!(content, method, options)
end
RUBY
# Subtracting 2 from the __LINE__ var for accurate error messages
class_eval(method_str, __FILE__, __LINE__-2)
alias_method_chain method_name, :error_field
end
end
# Creates a div wrapper with class 'form-group'
def group(options ={}, &block)
@template.form_group(options, &block)
end
# Shows the error messages for :base if any
def error_messages
@template.error_messages_for(object)
end
# Creates a div wrapepr with class 'input-group'
def input_group(options={}, &block)
@template.input_group(options, &block)
end
# Creates a fieldset tag
def fieldset(&block)
@template.form_fieldset(&block)
end
# Creates a legend tag
def legend(content, options ={})
@template.form_legend(content, options)
end
# Creates a div with class 'text-error' for displaying error messages
# on specific fields
def error_field(attribute, options = {})
return unless object.errors[attribute]
update_options_with_class!(options, 'text-danger')
content = object.errors[attribute].first
@template.content_tag(:div, content, options)
end
# Creates a small tag with class 'help-block' for displaying helpful
# text under a form input
def help_text(method, content, options = {})
return if object.errors[method].any?
update_options_with_class!(options, 'help-block')
@template.content_tag(:small, content, options)
end
# Creates a div wrapper with class 'checkbox'
def checkbox(&block)
@template.form_checkbox(&block)
end
# Creates a submit button with class 'btn btn-primary'
def submit(name, options ={})
update_options_with_class!(options, 'btn btn-primary')
super(name, options) << " " # add a space to the end
end
# Creates a cancel button to go back to a previous page.
def cancel(path, edit_path = nil, options ={})
options[:class] ||= ''
options[:class] << "btn btn-default"
options[:data] = { confirm: "Cancel without saving?"}
if edit_path && object.persisted?
path = edit_path
end
@template.link_to('cancel', path, options)
end
# Use this instead of fields_for
def custom_fields_for(record, options = {}, &block)
options.update(builder: CustomFormBuilder)
fields_for(record, options, &block)
end
private
# Add the error_field div to each field by default
def append_error_field_to_content!(content, method, options)
if object.errors[method].any? && !options[:error_field]
content << error_field(method)
end
content
end
# Update an options hash with class 'form-control'
def update_options_with_form_control_class!(options)
update_options_with_class!(options, 'form-control') unless (options && options[:class] == "radio_button")
end
# Update an options hash with a :class
def update_options_with_class!(options, klass)
@template.update_options_with_class!(options, klass)
end
end
# Use this instead of form_for
def custom_form(record, options = {}, &block)
options.update(builder: CustomFormBuilder)
form_for(record, options, &block)
end
# Creates a div wrapper with class 'input-group'
def input_group(options={}, &block)
content = capture(&block)
update_options_with_class!(options, 'input-group')
content_tag(:div, content, options)
end
# Creates a div wrapper with class 'form-group'
def form_group(options ={}, &block)
content = capture(&block)
update_options_with_class!(options, 'form-group')
content_tag(:div, content, options)
end
# Creates a fieldset with class from current_namespace (used internally in our project)
def form_fieldset(&block)
content_tag(:fieldset, capture(&block))
end
# Creates a legend tag
def form_legend(content, options ={})
content_tag(:legend, content, options)
end
# Creates a div wrapper with class 'input-group'
def form_input_group(&block)
content_tag(:div, capture(&block), class: "input-group")
end
# Creates a div wrapper with class 'checkbox'
def form_checkbox(&block)
content_tag(:div, capture(&block), class: "checkbox")
end
# Show the error messages on :base for a record
def error_messages_for(object)
if object.errors[:base].any?
message = object.errors[:base].first
%{<div class="text-danger">#{message}</div>}.html_safe
end
end
# Update an options hash with a given :class value
def update_options_with_class!(options, klass)
options[:class] ||= ''
options[:class] << " #{klass}"
options
end
end
#######
# Remember to add this lines in you application.rb
# config.action_view.field_error_proc = Proc.new { |html_tag, instance|
# "<div class='has-error'>#{html_tag}</div>".html_safe
# }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment