Created
February 12, 2009 23:49
-
-
Save dwg/62953 to your computer and use it in GitHub Desktop.
Custom form builder that labels elements, handles errors and wraps in appropriate markup
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
| # Drop the following in an initializer and enjoy consistency and less code when marking up forms. | |
| # <% form_for @user do |f| %> | |
| # <%= f.text_field :username %> | |
| # <%= f.check_box :terms_accepted, :label=>'I accept the terms' %> | |
| # <%= f.submit 'Save' %> | |
| # <% end %> | |
| # produces | |
| # <form ...> | |
| # <p> | |
| # <label for="user_username">Username</label><br/> | |
| # <input type="text" id="user_username" name="user[username]"/> | |
| # </p> | |
| # <p> | |
| # <input type="checkbox" id="user_terms_accepted" name="user[terms_accepted"/> | |
| # <label for="user_terms_accepted">I accept the terms</label> | |
| # </p> | |
| # <p> | |
| # <input type="submit" id="user_submit" value="Save" name="commit"/> | |
| # </p> | |
| # </form> | |
| # Will also append individual validation errors in a span following the erroring field (inside the p element) | |
| class LabellingFormBuilder < ActionView::Helpers::FormBuilder | |
| non_breaking_helpers = %w(check_box radio_button) | |
| breaking_helpers = field_helpers + | |
| %w(select collection_select country_select + time_zone_select) + | |
| %w(date_select time_select datetime_select) - | |
| %w(fields_for submit hidden_field label) - | |
| non_breaking_helpers | |
| breaking_helpers.each do |helper| | |
| src = <<-end_src | |
| def #{helper}(method, *args, &block) | |
| options = args.extract_options! | |
| build_shell(method, options, block, true) do |options| | |
| super method, *args << options | |
| end | |
| end | |
| end_src | |
| class_eval src, __FILE__, __LINE__ | |
| end | |
| non_breaking_helpers.each do |helper| | |
| src = <<-end_src | |
| def #{helper}(method, *args, &block) | |
| options = args.extract_options! | |
| build_shell(method, options, block, false) do |options| | |
| super method, *args << options | |
| end | |
| end | |
| end_src | |
| class_eval src, __FILE__, __LINE__ | |
| end | |
| def submit *args | |
| options = args.detect {|a| a.is_a?(Hash)} | |
| builder_options = extract_builder_options(options) | |
| @template.content_tag(:p, super, builder_options[:wrapper]) | |
| end | |
| private | |
| def build_shell method, options, block, breaking | |
| builder_options = extract_builder_options(options) | |
| errors = errors_for method | |
| if errors | |
| if options && options[:class] | |
| options[:class] << ' fieldWithErrors' | |
| elsif options | |
| options[:class] = 'fieldWithErrors' | |
| else | |
| options = {:class=>'fieldWithErrors'} | |
| end | |
| end | |
| element = yield options | |
| label = make_label method, builder_options[:label] | |
| if breaking | |
| content = [label, (@template.tag(:br) if label), element, errors].compact | |
| else | |
| content = [element, label, errors].compact | |
| end | |
| if block | |
| content = wrap(*content << @template.capture(&block) << builder_options[:wrapper]) | |
| @template.concat content, block.binding | |
| else | |
| wrap(*content << builder_options[:wrapper]) | |
| end | |
| end | |
| def make_label method, label_options | |
| case label_options | |
| when Hash | |
| label(method, label_options.delete(:text), label_options) | |
| when false | |
| nil | |
| else | |
| label(method, label_options) | |
| end | |
| end | |
| def extract_builder_options options | |
| options ? {:label=>options.delete(:label), :wrapper=>options.delete(:wrapper)} : {} | |
| end | |
| def wrap *args | |
| options = args.extract_options! | |
| @template.content_tag(:p, args.join, options) | |
| end | |
| def errors_for method | |
| if object && !object.errors.on(method).blank? | |
| errors = object.errors.on(method) | |
| errors = errors.is_a?(Array) ? errors.to_sentence : errors | |
| @template.tag(:br) + | |
| @template.content_tag(:span, errors, :class=>'fieldErrorMessage') | |
| end | |
| end | |
| end | |
| ActionView::Base.default_form_builder = LabellingFormBuilder | |
| ActionView::Base.field_error_proc = lambda {|html_tag, instance| html_tag} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment