【Rails】 검증 에러를 그 입력 필드의 가까이에 1개만 표시하고 싶었다.

이 기사에서 하고 싶은 일



이 기사에서는 Rails 양식의 오류 메시지를 함께 보내는 것이 아니라 각 입력 필드 근처에 내보내려면 어떻게해야할지 생각해 본 결과를 남겨 둡니다. (하고 싶은 것의 이미지는 아래쪽에 결과 이미지가 있습니다.)

환경



이 기사를 작성할 때 내 환경은 다음과 같습니다.
  • Ruby 2.7.1
  • Rails 6.0.3

  • 샘플 앱 전제



    이번에는 단순히 '상품명(name)'과 '가격(price)'을 속성으로 가진 상품(Product)을 관리하는 앱을 전제로 합니다.
    등록 양식에서 '상품명'과 '가격'을 입력할 수 있지만 각 유효성 검사에 걸린 경우 각 입력 필드에 오류 메시지를 표시할 수 있습니다.
    또, 하나의 인풋 필드에 복수의 밸리데이션 에러 메세지가 표시되는 것도 번역을 모르게 되어 버리기 때문에, 1개의 에러 메세지만을 표시하도록(듯이) 합니다.

    각각의 속성은 이하의 밸리데이션을 가지고 있는 것으로 합니다.

    이름(이름)
  • 입력되지 않음.
  • 20 문자 이내가 아니면 안된다.

  • 가격(price)
  • 입력되지 않음.
  • 100 이상이 아니면 안된다.
  • 100000 미만이 아니면 안된다.

  • 이 Product 모델을 관리하는 scaffold를 샘플 앱으로 합니다.
    $ rails g scaffold product name:string value:integer
    $ rails db:migrate
    

    app/models/product.rb
    class Product < ApplicationRecord
      validates :name,
        presence: true,
        length: {maximum: 20}
    
      validates :price,
        numericality: {
          greater_than_or_equal_to: 100,
          less_than: 100000
        }
    end
    

    이 앱에서는 /products/new/ 페이지에서 nameprice 의 입력을 잘못하면 페이지 상단에 오류가 표시됩니다.



    오류 메시지 표시는 app/views/products/_form.html.erb 부분 템플릿에 정의되어 있습니다.

    app/views/products/_form.html.erb
    <%= form_with(model: product, local: true) do |form| %>
      <%# ここからエラーメッセージ %>
      <% if product.errors.any? %>
        <div id="error_explanation">
          <h2><%= pluralize(product.errors.count, "error") %> prohibited this product from being saved:</h2>
    
          <ul>
            <% product.errors.full_messages.each do |message| %>
              <li><%= message %></li>
            <% end %>
          </ul>
        </div>
      <% end %>
      <%# ここまでエラーメッセージ %>
    
      <div class="field">
        <%= form.label :name %>
        <%= form.text_field :name %>
      </div>
    
      <div class="field">
        <%= form.label :price %>
        <%= form.number_field :price %>
      </div>
    
      <div class="actions">
        <%= form.submit %>
      </div>
    <% end %>
    

    구현 예



    app/views/products/_form.html.erb 를 편집하여 name 의 오류 메시지가 name 의 입력 필드 아래에, price 의 오류 메시지가 price 의 입력 필드 아래에 표시되도록 합니다.

    현재 상태 app/views/products/_form.html.erb
  • product.errors.any? 에서 모든 속성 중 하나라도 오류가 있는지 확인
  • product.errors.full_messages에서 모든 속성에 대한 오류 메시지 표시

  • 하고 있습니다.
    즉, 속성 단위에 에러 유무를 판별해, 속성 단위에 에러 메세지를 취득할 수 있으면 좋을 것 같습니다.

    속성 단위에 에러 유무를 판별한다 include?


    include?(attribute) 를 사용하여 속성 단위의 오류 유무를 판별할 수 있습니다.attribute 에 관한 에러가 모델 오브젝트의 에러 오브젝트에 포함되고 있는 경우 true 를, 그렇지 않은 경우는 false 를 반환하는 메소드입니다.
    참고

    속성 단위로 오류 메시지 얻기 full_messages_for


    full_messages 로 모든 속성에 관한 에러 메세지를 취득할 수 있었습니다만, full_messages_for(attribute) 를 사용하는 것으로 특정의 속성에 관한 에러 메세지를 취득할 수가 있습니다.
    참고

    이것들을 조합하는 것으로 app/views/products/_form.html.erb 를 이하와 같이 편집합니다.

    app/views/products/_form.html.erb
      <%= form_with(model: product, local: true) do |form| %>
        <% if product.errors.any? %>
           <div id="error_explanation">
            <h2><%= pluralize(product.errors.count, "error") %> prohibited this product from being saved:</h2>
    -
    -       <ul>
    -         <% product.errors.full_messages.each do |message| %>
    -           <li><%= message %></li>
    -         <% end %>
    -       </ul>
          </div>
        <% end %>
    
        <div class="field">
          <%= form.label :name %>
          <%= form.text_field :name %>
    +     <% if product.errors.include?(:name) %>
    +       <p style="color: red;"><%= product.errors.full_messages_for(:name).first %>
    +     <% end %>
        </div>
    
        <div class="field">
          <%= form.label :price %>
          <%= form.number_field :price %>
    +     <% if product.errors.include?(:price) %>
    +       <p style="color: red;"><%= product.errors.full_messages_for(:price).first %>
    +     <% end %>
        </div>
    
        <div class="actions">
          <%= form.submit %>
        </div>
      <% end %>
    

    이번은 full_messages_for 의 에러 메세지의 배열로부터 1 번째의 메세지만을 표시하도록(듯이) first 로 배열의 선두의 아이템을 취득하고 있습니다.
    이 변경은 오류 메시지의 표시를 다음과 같이 변경합니다.



    하고 싶을 수 있었습니다.

    Reference


  • ActiveModel::Errors
  • 좋은 웹페이지 즐겨찾기