ruby-on-railserror-handlingsimple-formnested-forms

Rails - Simple Form Bootstrap - show full text of error inline - (or anywhere)


I am struggling to figure out how to use simple form with rails 5 so that I can show full error messages in my forms. All I get is a box at the top with a message that says:

Please review the problems below:  

Instead of a list of problems or any highlighted form fields - i don't get anything.

I have followed the advice in this post and replaced all references to error in my initialiser with 'full_error', but that only solves the problem for the parent form - nested fields still just get an incomplete error message. Does anyone know how to identify errors in simple form?

# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
  config.error_notification_class = 'alert alert-danger'
  config.button_class = 'btn btn-green'
  config.boolean_label_class = nil

  config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.use :placeholder
    b.optional :maxlength
    b.optional :pattern
    b.optional :min_max
    b.optional :readonly
    b.use :label, class: 'control-label'

    b.use :input, class: 'form-control'
    b.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
  end

  config.wrappers :vertical_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.use :placeholder
    b.optional :maxlength
    b.optional :readonly
    b.use :label, class: 'control-label'

    b.use :input
    b.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
  end

  config.wrappers :vertical_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.optional :readonly

    b.wrapper tag: 'div', class: 'checkbox' do |ba|
      ba.use :label_input
    end

    b.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
  end

  config.wrappers :vertical_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.optional :readonly
    b.use :label, class: 'control-label'
    b.use :input
    b.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
  end

  config.wrappers :horizontal_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.use :placeholder
    b.optional :maxlength
    b.optional :pattern
    b.optional :min_max
    b.optional :readonly
    b.use :label, class: 'col-sm-3 control-label'

    b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
      ba.use :input, class: 'form-control'
      ba.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
    end
  end

  config.wrappers :horizontal_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.use :placeholder
    b.optional :maxlength
    b.optional :readonly
    b.use :label, class: 'col-sm-3 control-label'

    b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
      ba.use :input
      ba.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
    end
  end

  config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.optional :readonly

    b.wrapper tag: 'div', class: 'col-sm-offset-3 col-sm-9' do |wr|
      wr.wrapper tag: 'div', class: 'checkbox' do |ba|
        ba.use :label_input
      end

      wr.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
      wr.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
    end
  end

  config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.optional :readonly

    b.use :label, class: 'col-sm-3 control-label'

    b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
      ba.use :input
      ba.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
    end
  end

  config.wrappers :inline_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.use :placeholder
    b.optional :maxlength
    b.optional :pattern
    b.optional :min_max
    b.optional :readonly
    b.use :label, class: 'sr-only'

    b.use :input, class: 'form-control'
    b.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
  end

  config.wrappers :multi_select, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
    b.use :html5
    b.optional :readonly
    b.use :label, class: 'control-label'
    b.wrapper tag: 'div', class: 'form-inline' do |ba|
      ba.use :input, class: 'form-control'
      ba.use :full_error, wrap_with: { tag: 'span', class: 'help-block' }
      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }
    end
  end
  # Wrappers for forms and inputs using the Bootstrap toolkit.
  # Check the Bootstrap docs (http://getbootstrap.com)
  # to learn about the different styles for forms and inputs,
  # buttons and other elements.
  config.default_wrapper = :vertical_form
  config.wrapper_mappings = {
    check_boxes: :vertical_radio_and_checkboxes,
    radio_buttons: :vertical_radio_and_checkboxes,
    file: :vertical_file_input,
    boolean: :vertical_boolean,
    datetime: :multi_select,
    date: :multi_select,
    time: :multi_select
  }
end

FORM

<%= simple_form_for(@proposal) do |f| %>
  <%= f.error_notification %>

 <% @proposal.errors.full_messages.each do |msg| %>
    <%= li= msg %>
 <% end %>



  <div class="form-inputs" style="margin-bottom: 50px">
  <!--   General Organisation details -->
    <div class="row">
      <div class="col-md-12">
        <%= f.input :title, :label => "Title" %>

      </div>
    </div>
    <div class="row">
      <div class="col-md-12">
        <%= f.input :byline, :label => "Tagline" %>
      </div>
    </div>

    <div class="row">
      <div class="col-md-12">
        <%= f.input :description, as: :text, :label => "Outline your proposal", input_html: { rows: 15 } %>
      </div>
    </div>




  <!--   Package: :ethics considerations -->
  <div class="row">
    <div class="col-md-12">
      <div class="form_title">Research Ethics</div>
    </div>
  </div>

  <div class="row">
    <div class="col-md-12">

    <%= f.simple_fields_for :ethics do |f| %>
      <%= f.error_notification %>
        <%= render 'package/ethics/ethics_fields', f: f %>

    <% end %>

    <%= link_to_add_association 'Add another ethics issue', f, :ethics, partial: 'package/ethics/ethics_fields' %>
    </div>
  </div>


  </div>

  <div class="row">
    <div class="col-md-10 col-md-offset-1" style="margin-top: 50px">
      <div class="form-actions">
        <%= f.button :submit %>
      </div>
    </div>
  </div>
<% end %>

NESTED FIELDS

<div class="nested-fields">

    <%# @proposal.ethics.errors.full_messages.each do |e| %>
        <li><%#=  e  %></li>
    <%# end %>
    <div class="form-inputs">
      <div class="row">
          <div class="col-md-12 ">
          <%= f.select :category, [ "Risk of harm", "Informed consent", "Anonymity and Confidentiality", "Deceptive practices", "Right to withdraw"], { label: "Principle" }, id: "main_category" %>
          <%= f.select :subcategory, [], {}, id: "sub_category", disabled: true %>
          <%= f.input :consideration, as: :text, :label => "Identify the ethics considerations?",  :input_html => {:rows => 8} %>
          <%= f.input :plan, as: :text, :label => "How will these considerations be managed?",  :input_html => {:rows => 8} %>
        </div>
      </div>
  </div>
  <div class="row">
      <div class="col-md-6" style="margin-top: 20px; margin-bottom: 50px">
        <%= link_to_remove_association 'Remove this ethical issue', f %>
      </div>

  </div>
</div>

Solution

  • It looks like you are getting haml and erb mixed up in your error displaying code.

    This:

     <% @proposal.errors.full_messages.each do |msg| %>
       <%= li= msg %>
     <% end %>
    

    should look more like this:

    <% if @proposal.errors.any? %>
    <ul>
      <% @proposal.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
    <% end %>
    

    I'm guessing you are using Cocoon so you'll need to set that up correctly but there is a gotcha when using Rails 5.

    You'll need something like the following in your models:

    class Proposal < ApplicationRecord
      has_many: :ethics, inverse_of: :proposal
      accepts_nested_attributes_for :ethics ...
    ...
    

    and

    class Ethic < ApplicationRecord
      belongs_to :proposal, inverse_of: :ethics
    ...
    

    note the has_many .. inverse_of clause which works round a bug in Rails 5.

    If you setup the accepts_nested_attributes_for clause correctly I think @proposal.errors should contain all the errors for the form.