# Provides error wrapping and some extra helper methods to working with labels and translations # easier. # # Apart from adding new helper methods, this class overwrites the standard *_field # and *_select helpers. If one of the fields has an error, the validation errors for # that method are added just after the field. See {Skyline::FormBuilderWithErrors#wrap_with_error} # for more information on what get's added. class Skyline::FormBuilderWithErrors < ActionView::Helpers::FormBuilder # Custom InstanceTag class from which we can extract the ID # @private class CustomInstanceTag < ActionView::Helpers::InstanceTag def to_id(options={}) options = options.stringify_keys name_and_id = options.dup add_default_name_and_id(name_and_id) options.delete("index") name_and_id["id"] end end unless ActiveSupport::Dependencies.load_once_path?(__FILE__) ActiveSupport::Dependencies.autoloaded_constants << "Skyline::FormBuilderWithErrors::CustomInstanceTag" end # Overwrite all standard helpers with a wrapped version. (field_helpers - %w(label fields_for radio_button check_box hidden_field) + %w(select collection_select time_zone_select date_select time_select datetime_select)).each do |selector| src = <<-end_src def #{selector}(method, *parms) wrap_with_error(method,*parms) do |parms| super(method,*parms) end end end_src class_eval src, __FILE__, __LINE__ end # An improved version of the standard label helper it will append the class "invalid" # # @see ActionView::Helpers::FormBuilder#label # # @return [String] def label(method, text = nil, options = {}) if @object.errors[method] super(method,text,options.merge(:class => "invalid #{options[:class]}".strip)) else super end end # Same as label but automatically translates the field name # # @see Skyline::FormBuilderWithErrors#t # # @return [String] def label_with_text(method, options = {}) self.label(method, self.t(method), options) end # Special field to handle automated reordering of elements ( # with help of {Skyline::NestedAttributesPositioning} module) # # @example Usage: # <% article_form.fields_for "sections_attributes", section, :index => 5 do |s| %> # <%= s.hidden_field :id unless s.object.new_record? %> # <%= s.hidden_field :_destroy, :class => "delete" %> # <%= s.positioning_field %> # ... # <% end %> # # @example Results in the following fields: # # # # def positioning_field name = self.object_name.sub(/\[([^\]]*)_attributes\]$/, "[\\1_position]") + "[]" value = options[:index] raise "No options[:index] defined, need one to use for ordering correctly." unless value tag_id = name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "") + value.to_s @template.hidden_field_tag name, value, :id => tag_id end # Does the object have any errors set on an attribute. # # @param attribute [String,Symbol] The attribute to check for errors # @return [Boolean] def has_error?(attribute) @object.errors[attribute].present? end # Add the index of the current scope to the object_name if an index is used. # # @return [String] The object_name with an optional index added def object_name_with_index if options[:index].present? self.object_name + "[#{options[:index]}]" else self.object_name end end # Wrap an input element with errors, also appends "invalid" to the helpers # class. See {Skyline::FormBuilderWithErrors#fieldset_errors} on how # the errors are added. # # @param method [Symbol,String] The method/attribuet to check for errors # @param parms [] # @param options [Hash] Options to pass to the original field helper, the key :text_suffix is stripped off # # @option options :text_suffix () If you want text before the errors are # added you can set it by using text_suffix. This will be added immideately after the field. # # @yield [parameters] The return value of the block will be wrapped with the error # @yieldparam [Hash] parameters The parameters to pass to the block (is the same as the parms of the method) # # @return [String] def wrap_with_error(method,*parms,&block) options = parms.extract_options! method_options = options.except(:text_suffix) if @object.errors[method] method_options[:class] ||= "" method_options[:class] << " invalid" end parms << method_options if method_options.any? html = yield(parms) html << options[:text_suffix] if options[:text_suffix] if @object.errors[method] html + fieldset_errors(method) else html end end # Generates a list of errors. Each error is wrapped in a #