Search code examples
ruby-on-railsjsonajaxruby-on-rails-5

Rails Form_with display inline error message


I am trying to display the error message inline below the form elements in my rails application.

I did the following.

Controller

def create
    @faculty = Faculty.new(faculty_params)
    respond_to do |format|
        if @faculty.save
            format.html { redirect_to admin_faculties_path, notice: 'Faculty was successfully created.'}
            format.json {render json: @faculty, status: :created, location: @faculty}
        else
            format.html {render '_form'}
            format.json {render json: @faculty.errors, status: :unprocessable_entity }
        end
    end
end

Form

    <% form_with(model: [:admin, @faculty]) do |f| %>
<div class="card-title">Add Faculty</div>
    <div class="form-group">
        <%= f.label :faculty_name, class: "form-label" do %>
        Faculty Name <span class="form-required">*</span>
        <% end %>
        <%= f.text_field :faculty_name, class: "form-control", placeholder: "Faculty Name", required: true %>
    </div>
<%= f.submit class: "btn btn-primary" %>

As I have some validations rules in Model, it checks the validations and stop the form from submitting if the validations occurs. As, this is the form_with form all the requests are handled by Ajax. So, I am looking to display the validations errors below the each form elements. Please suggest me with examples so that I can complete this task.


Solution

  • You could check for every attribute's errors on your form fields and if that attribute has any error just show them

    <% form_with(model: [:admin, @faculty]) do |f| %>
      <div class="card-title">Add Faculty</div>
      <div class="form-group">
        <%= f.label :faculty_name, class: "form-label" do %>
          Faculty Name <span class="form-required">*</span>
        <% end %>
        <%= f.text_field :faculty_name, class: "form-control", placeholder: "Faculty Name", required: true %>
        <% if @faculty.errors[:name].any? %>
          <ul>
            <% @faculty.errors[:name].each do |message| %>
              <li><%= message %></li>
            <% end %>
          </ul>
        <% end %>
      </div>
    <%= f.submit class: "btn btn-primary" %>
    

    A better solution could be refactor that on a helper:

    Helper

    module FacultyHelper
    
      def inline_errors(model, model_attribute)
        result = ""
        if model.errors[model_attribute].any?
             model.errors[model_attribute].each do |message|
                result += "<li>#{message}</li>"
            end
        end
        return "<ul>#{result}</ul>".html_safe
      end
    end
    

    View[UPDATE: wrapper for name errors]

    <% form_with(model: [:admin, @faculty]) do |f| %>
      <div class="card-title">Add Faculty</div>
      <div class="form-group">
        <%= f.label :faculty_name, class: "form-label" do %>
          Faculty Name <span class="form-required">*</span>
        <% end %>
        <%= f.text_field :faculty_name, class: "form-control", placeholder: "Faculty Name", required: true %>
        <div id="user_name_errors">
           <%= inline_errors(@faculty, :name) %>
        </div>
      </div>
    <%= f.submit class: "btn btn-primary" %>
    

    For remote: true, you need to define on your controller format.js

    Controller

    def create
        @faculty = Faculty.new(faculty_params)
        respond_to do |format|
            if @faculty.save
                format.html { redirect_to admin_faculties_path, notice: 'Faculty was successfully created.'}
                format.json {render json: @faculty, status: :created, location: @faculty}
                format.js
            else
                format.html {render '_form'}
                format.json {render json: @faculty.errors, status: :unprocessable_entity }
                format.js
            end
        end
    end
    

    Create a corresponding js.erb view

    create.js.erb

    $("#user_name_errors").html("<%= inline_errors(@faculty, :name) %>");