【Rails】validationのエラーメッセージを個別に表示する方法

#この記事のゴール
この記事では公式リファレンスを参照し、以下の画像のようにValidationのエラーメッセージを各フォームの近くに個別に表示する方法を記載しております。
![スクリーンショット 2021-02-02 3.12.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/91113d48-b479-8c95-b1b1-45c3b64aa45a.png)

#環境
この記事では以下の環境にて実装を行なっております。

* ruby 2.6.5p114 (2019-10-01 revision 67812)
* Rails 6.0.3.4
* Bundler version 2.1.4

![スクリーンショット 2021-02-01 20.42.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/b2af56a1-c1c2-1198-9083-bf28f38cd521.png)

#実装過程

###前提

```ruby:Terminal
rails g scaffold Post title:string content:text
```

今回はRailsでscaffoldを使用しPostモデルを作成。
Postモデルは「string型のtitleカラム」と「text型のcontentカラム」を持っています。

```ruby:post.rb
class Post < ApplicationRecord
  validates :title, presence: true
  validates :title, length: { minimum: 2, maximum: 30 }
  validates :content, presence: true
end
```

それぞれのカラムは以下のvalidationを持っています。
#####title（タイトル）

* 入力必須
* タイトルは２文字以上30文字以下で入力

#####content（内容）

* 入力必須

###Validationエラーをまとめて表示する（よくある表示方法）
![スクリーンショット 2021-02-02 1.17.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/01f88ebc-e44b-31ec-6020-7d5ac2532d81.png)
scaffoldでは上記のように、デフォルトでvalidationのエラーメッセージがまとめて表示されるようになっています。
普段の実装でもこのような表示方法を採用している方は多いと思います。

上記のエラーメッセージは以下のように定義しています。

```app/views/post/_form.html.haml
  - if post.errors.any?
    #error_explanation
      %h2
        = pluralize(post.errors.count, "error")
        prohibited this post from being saved:
      %ul
        - post.errors.full_messages.each do |message|
          %li= message
```

① まず変数postに対しerrorsメソッドでエラー文に関するハッシュを取得します。
![スクリーンショット 2021-02-02 1.36.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/57ebfc5b-28e6-4f51-5b9d-e65d30d1b1d0.png)
② その値に対し[full_messages](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-full_messages)メソッドを使用することによりエラーメッセージを配列で取得します。
![スクリーンショット 2021-02-02 1.41.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/c97c3156-1bf7-a06b-f6f8-1b6726e3a9cc.png)

```
・full_messagesメソッド

全てのエラーメッセージを配列で取得するメソッド
```

③ 配列で取得した値にeachメソッドを使用することでエラー文をそれぞれ表示します。

###Validationエラーを個別に表示する（今回の主題）
![スクリーンショット 2021-02-02 1.54.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/fcef6aa6-99a4-8775-8480-d1bcecdefbc0.png)
上記のエラーメッセージは以下のように定義しています。

```app/views/post/_form.html.haml
= form_with(model: post, local: true) do |form|
  .field
    = form.label :title
    = form.text_field :title

    //以下がtitleカラムに関するエラーメッセージの表示
    - if post.errors.include?(:title)
      %p= post.errors.full_messages_for(:title).first

  .field
    = form.label :content
    = form.text_area :content

    //以下がcontentカラムに関するエラーメッセージの表示
    - if post.errors.include?(:content)
      %p= post.errors.full_messages_for(:content).first

  .actions
    = form.submit
```
① まず[include?(attribute)](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-include-3F)メソッドを使用し、引数に入っているカラムに関するエラーが存在するかを判別します。
[include?(attribute)](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-include-3F)メソッドは[key?(attribute)](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-key-3F)メソッドや[has_key?(attribute)](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-has_key-3F)メソッドでも代用可能です。

```
・include?(attribute)メソッド
・key?(attribute)メソッド
・has_key?(attribute)メソッド

errors.messagesで得た配列の中に、引数内のカラムに関するエラーが存在する
場合はtrueを返し、存在しない場合はfalseを返すメソッド
```

② エラーメッセージが存在する場合は、[full_messages_for(attribute)](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-full_messages_for)メソッドで引数内のカラムに関するエラーを配列で取得します。

```
・full_messages_for(attribute)メソッド

引数内のカラムに関するエラーメッセージを配列で取得するメソッド
```

③ 上記で取得した配列にfirstを使用することでエラー文を個別に表示します。

###UIを整える

① semantic-uiをインストールする

Gemfileに以下を追加し、`bundle install`します。

```ruby:Gemfile
gem 'semantic-ui-sass'
```

② semantic-uiを使用できるようにする

application.scssに以下を追加します。

```app/assets/stylesheets/application.scss
 @import "semantic-ui"
```

これでsemantic-uiを使えるようになったので、エラーメッセージのスタイルを整えていきます。

③ semantic-uiでスタイル調整
![スクリーンショット 2021-02-02 3.12.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/0c341dfe-9a92-d876-53c3-658a82ae5d30.png)
semantic-uiを使用し、以下のようにクラスを使ってスタイルを当てたら上記のようなエラーメッセージを表示することができます。

```app/views/post/_form.html.haml
= form_with(model: post, local: true) do |form|
  .ui.form
    .field.required
      = form.label :title, 'タイトル'
      = form.text_field :title
      - if post.errors.include?(:title)
        %p.ui.pointing.red.basic.label
          %i.attention.circle.icon
          = post.errors.full_messages_for(:title).first
    .field.required
      = form.label :content, '内容'
      = form.text_area :content
      - if post.errors.include?(:content)
        %p.ui.pointing.red.basic.label
          %i.attention.circle.icon
          = post.errors.full_messages_for(:content).first
    .actions
      = form.submit '保存', class: 'ui primary button'
```

#参考文献

* [ActiveModel::Errors < Object](https://api.rubyonrails.org/classes/ActiveModel/Errors.html)