Skip to content

Allow for override of field_error_proc in the FormBuilder #39522

@thebravoman

Description

@thebravoman

Create a form with a custom builder. Generate input fields with additional labels and classes in the form builder. When an active record validation error occurs the ActionView::Base.field_error_proc is called that wraps the field with a span and a class.

But we should be able to override field_error_proc in the FormBuilder since this is where we are building the forms. In my case the field_error_proc was even defined by another engine, so it made it more difficult to debug why an additional span was added. The additional span was breaking the functionality because of the way the toggle checkboxes were generated.

Steps to reproduce

The builder in my case has the following method.

def collection_check_boxes method, collection, value_method, text_method, options = {}, html_options = {}
  super(method, collection, value_method, text_method, options, html_options) do |b|
    @template.content_tag :div, class: "form-group" do 
      b.label(class: "d-flex align-items-center justify-content-between", for: nil) do
        @template.content_tag(:span, b.text) +
        @template.content_tag(:div, class: "u-check") do 
          b.check_box(class: "g-hidden-xs-up g-pos-abs g-top-0 g-right-0")+
          @template.content_tag(:div, class: "u-check-icon-radio-v8") do 
            @template.content_tag(:i, "", class: "fa", "data-check-icon": "")
          end
        end
      end 
    end
  end
end

Actual behavior

It generates

<div class="form-group">
  <label class="d-flex align-items-center justify-content-between">
    <span>organizations_manage</span>
    <div class="u-check">
      <input class="g-hidden-xs-up g-pos-abs g-top-0 g-right-0" type="checkbox" value="organizations_manage" checked="checked" name="organizations_user[roles_name][]" id="organizations_user_roles_name_organizations_manage" />
      <div class="u-check-icon-radio-v8">
        <i class="fa" data-check-icon=""></i>
      </div>
    </div>
  </label>
</div>

This works very well until there is an error in the form. Then the generated html is

<div class="form-group">
  <label class="d-flex align-items-center justify-content-between">
    <span>organizations_manage</span>
    <div class="u-check">
      <span class="fieldWithErrors">
        <input class="g-hidden-xs-up g-pos-abs g-top-0 g-right-0" type="checkbox" value="organizations_manage" checked="checked" name="organizations_user[roles_name][]" id="organizations_user_roles_name_organizations_manage" />
      </span>
      <div class="u-check-icon-radio-v8">
        <i class="fa" data-check-icon=""></i>
      </div>
    </div>
  </label>
</div>

Notice the additional <span class="fieldWithErrors">. Because of this span the checkbox is no longer working.

If there is an error we are calling Base.field_error_proc

actionview-6.0.3.1/lib/action_view/helpers/active_model_helper.rb
   25:         tag_generate_errors?(options) ? error_wrapping(super) : super
   26:       end
   27: 
   28:       def error_wrapping(html_tag)
   29:         if object_has_errors?
=> 30:           Base.field_error_proc.call(html_tag, self)
   31:         else
   32:           html_tag
   33:         end
   34:       end

and in my case this field_error_proc was defined in another engine used by the host app- refinery

refinerycms-d1a8db3061d4/core/lib/refinery/core/engine.rb
   34:       config.to_prepare(&method(:refinery_inclusion!).to_proc)
   35: 
   36:       # Wrap errors in spans
   37:       config.to_prepare do
   38:         ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
=> 39:           ActionController::Base.helpers.content_tag(:span, html_tag, class: "fieldWithErrors")
   40:         end
   41:       end
   42: 

Expected behavior

I am sure there are ways to hack around this in the collection_check_boxes and generate the field in a different way, but I think active model helper should first call a method of the FormBuilder and probably after that delegate to Base.field_error_proc. This would allow us to override the error classes for a specific form. Because if I now just disable this proc I would break the functionality provided by a different engine.

System configuration

Rails version: 6.0.3.1

Ruby version: 2.6.5

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions