【Ruby on Rails】バリデーションエラーメッセージの実装(初学者向け）

#実装したいこと

・新規投稿および編集の失敗の際、エラーメッセージを表示させる。
・成功の際には遷移先の画面にフラッシュメッセージを表示させる。

エラーメッセージのイメージはこんな感じ。
あくまでイメージなので、説明の際に用いるコードは別物。（こちらは以前作成したアプリのスクショ）

![スクリーンショット 2021-01-30 16.30.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/936377/78628951-fe17-6b55-018e-341b5cc3da62.png)


#エラーメッセージの実装

投稿用のモデルは`Postモデル`とし、`Userモデル`と1対多の想定で説明を進める。

## 1.モデルにバリデーションをかける

```rb:app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user
  #空の投稿を不可にする
  validates :title, presence: true
  validates :content, presence: true
end
```

## 2.アクションに処理内容を追加

投稿失敗の際の画面表示の方法として、`redirect_to`によるアクションを介する画面遷移をしてしまうと、作成していたデータは全て白紙の状態にされてしまう。
そのため投稿データの作成に失敗した場合には、`render`メソッドを用いてアクションを介さず新規投稿画面の表示のみを行う。

```rb:app/controllers/posts_controller.rb
  def create
    @post = Post.new(post_params)
    @post.save
    redirect_to posts_path
  end
```
↓　以下のように編集  

```rb
  def create
    @post = Post.new(post_params)
    if @post.save 
      #保存が成功した場合は一覧ページに遷移
      redirect_to posts_path
    else
      #失敗した場合は直前の画面を表示
      render "new" 
    end
  end 
```
**updateアクションにも同様の編集を行う**

##3.エラーメッセージファイルの作成

###補足説明

バリデーションエラーで保存に失敗した場合、Railsのデフォルトの仕様により自動的にエラーメッセージが生成され、`インスタンス変数.errors.full_messages`の中にエラー内容が配列で入ることとなる。
このことは`rails console`で不適正なデータを作成して確認してみると実感として理解しやすい。

```terminal:terminal
$ rails c
:
[1] pry(main)> post = Post.new(title:"", content:"")
[2] pry(main)> post.errors.full_messages => [] #保存失敗の前は空の配列の状態
[3] pry(main)> post.save => false
[4] pry(main)> post.errors.full_messages => ["Titleを入力してください", "Contentを入力してください"] #保存に失敗した時点で、配列にエラーメッセージが含まれる
```
このように生成されたエラーメッセージを、タグを用いて投稿フォーム内に埋め込む。

今回はエラー表示のコードを部分テンプレートとして用意し、フォーム内にrenderで埋め込む。
renderで埋め込むエラーメッセージのファイルがこちら。

```erb:app/views/layouts/_errors_messages.html.erb
<% if model.errors.any? %>
  <div class="alert alert-warning">
    <ul>
      <% model.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>
```
ちなみに上記コードのように`model.errors.full_messages`とすることで、どのモデルにも対応できるようになる。

もちろん部分テンプレートとしてではなく、各モデルの新規投稿フォームに直接記述するのであれば、`@post.errors.full_messages`のような書き方でも問題ない。

## 4.部分テンプレート（パーシャル）をフォーム内に挿入する

```erb:app/views/posts/new.html.erb
:
<%= form_with model:@post, local:true do |f|%>
<%= render 'layouts/error_messages', model: f.object %>
  <div class="form-group">  
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>

  <div class="form-group">        
    <%= f.label :content %>
    <%= f.text_area :content %>
  </div>

    <%= f.submit "投稿する" %>
<% end %>
:
```

２行目のこの部分で、部分テンプレート内で使用している変数`model`にフォームのオブジェクト(`f.object`)を渡している。

```erb
<%= render 'layouts/error_messages', model: f.object %>
```

#フラッシュメッセージの実装
## 1.作成が成功した際のフラッシュメッセージの挿入

今までの手順で作成が失敗した場合のエラーメッセージの表示ができるようになったので、今度は作成成功の際のフラッシュメッセージを表示させるようにする。
データ更新、データ削除の際のフラッシュメッセージも同様に記述する。

```rb:app/controllers/posts_controller.rb
:
  def create
    @post = Post.new(post_params)
    if @post.save 
      flash[:notice] = "投稿を作成しました" 
      redirect_to posts_path
    else
      render "new" 
    end
  end 
:
  def update 
    @post = Post.find(params[:id])
    if @post.update(post_params)
      flash[:notice] = "投稿を更新しました"
      redirect_to post_path(@post)
    else
      render "edit"
    end
  end
:
  def destroy
    @post = Post.find(params[:id])
    @post.destroy
    flash[:notice] = "投稿を削除しました"
    redirect_to posts_path
  end
:
```
## 2.ビューにフラッシュメッセージのコードを挿入

ビューファイルに`flash[:notice]`を埋め込む。
基本的にどの画面でも対応できるようにするため、`application.html.erb`にコードを記述するのが望ましい。

```erb:app/views/layouts/application.html.erb
  <body>
    <header>
       :
    </header>
    <% if flash[:notice] %> 
     <div class="flash">
        <%= flash[:notice] %>
      </div>
    <% end %>    
    <%= yield %>
  </body>
```

以上の手順でフラッシュメッセージの実装が完了。
ちなみにフラッシュメッセージには`flash[:notice]`の他に`flash.now[:notice]`など記述方法がいくつかあり、機能が異なるので用途に応じて使い分けることをお勧めする。

参考記事：[flashとflash.nowの違いを検証してみた](https://qiita.com/taraontara/items/2db82e6a0b528b06b949)

**記載内容に間違いがあった場合はご指摘頂けると嬉しいです。
ご連絡お待ちしております。**