【Rails】flashメッセージを使用して簡易メッセージを表示させる詳しい方法と解説

## はじめに
flashメッセージとは、アクション実行後に簡単なメッセージを表示させるRailsの機能です。
なお、deviseの機能（ユーザー作成やログイン等）を使用した場合とそうでない場合で実装方法が違いますが、今回は**deviseを使用していない場合**のflashメッセージの表示方法について説明していきます。
また前提として、今回はview部分をhaml記法にて記述します。

## やりたいこと
Sendボタン押下にてメッセージを投稿した際に、flashメッセージが画面上部に表示されるよう実装します。今回はメッセージの投稿成功時と失敗時でメッセージが変わるようにします。

・投稿に**成功**した場合
水色の「メッセージが送信されました」部分がflashメッセージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389470/4c976f28-aee1-0406-faec-bf014146c63b.png)

・投稿に**失敗**した場合
オレンジ色の「メッセージを入力して下さい」部分がflashメッセージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389470/72818414-e518-daab-7b61-a76fbe8c1629.png)

## 「flash」を使用してflashメッセージを表示させてみる
大まかな流れは以下のとおりです。

1. controllerを編集する
2. viewを編集する
3. CSSを追加する

### 1. controllerを編集する
flashメッセージ使用するためには、「FlashHash」というオブジェクトを使用します。
こちら、名前の通りハッシュオブジェクトです。
後述しますが、処理が成功した場合には[:notice]キーを、失敗した場合には[:alert]キーを使用します。


「Flash」や「FlashHash」についての詳細はこちらに書いてありますので、「Flash」でサイト内検索してみて下さい。
*参考：[Ruby on Rails ガイド](https://railsguides.jp/action_controller_overview.html#flash)*


さて、本題に入っていきます。
結論から言うと、完成形の例はこんな感じです。
これから一つ一つ見ていきます。

```messages_controller.rb
1   def create
2     @message = @group.messages.new(message_params)
3     if @message.save
4       redirect_to group_messages_path(@group), notice: 'メッセージが送信されました'
5     else
6       flash.now[:alert] = 'メッセージを入力してください。'
7       render :index
8     end
9   end
```

#### 投稿に成功した場合
例でいうと、3〜4行目が投稿に成功した場合の処理になります。
ただ、以下のほうがわかりやすいかと思うので、例とは少し違う書き方で説明します。

```messages_controller.rb
3   if @message.save
4     # 投稿に成功した場合
5     flash[:notice] = 'メッセージが送信されました'  #ここが例と違う
6     redirect_to group_messages_path(@group) #ここが例と違う
```
5行目でflashオブジェクトのキーである[:notice]に表示したいメッセージをvalueとして追加しています。
6行目ではメッセージ登録後、<font color="Red">リダイレクト</font>をしてindex.htmlに遷移しています。

結果は同じですが、上記の処理は一行で記述できるので、完成形の例では以下のようにしています。こっちのほうがスッキリです。

```messages_controller.rb
4    if @message.save
5      redirect_to group_messages_path(@group), notice: 'メッセージが送信されました'
```
#### 投稿に失敗した場合
5〜7行目が投稿に失敗した場合の処理になります。

```messages_controller.rb
5    else
6      flash.now[:alert] = 'メッセージを入力してください。'
7      render :index
```

6行目でflashオブジェクトのキーである[:alert]に表示したいメッセージをvalueとして追加しています。
成功時とは違い、<font color="Red">flash.now</font>を使用しています。（※）
7行目ではメッセージ登録後、<font color="Red">render</font>をしてindex.htmlに遷移しています。

※flashとflash.nowの使い分けについては後述

### 2. viewを編集する
controllerが完成したので、application.htmlを編集して画面に表示します。
まず最初に、追加する箇所はこんな感じ。

```application.html.haml
~~~~~~~~~~~~~~~~~~略~~~~~~~~~~~~~~~~~~
1  %body
2    #----------ここから----------
3    .notifications
4      - flash.each do |key, value|
5        = content_tag(:div, value, class: key)
6    #--------ここまでを追加--------
7    = yield
```
3~5行目のflashメッセージ部分は、HTMLに変換されると以下のような形になります。

```application.html
<div class="notifications">
 <div class="notice">メッセージが送信されました</div>
</div>
```

application.htmlはどのファイルを読み込む場合でも読み込まれる共通のviewです。
「= yeild」にて他のviewファイル(index.htmlとか)を読み込む事によって、そのviewファイルの内容が画面に表示されます。
今回は画面の上部にflashメッセージを表示させたいので、「= yeild」の前（他のviewファイルを読み込む前）にflashメッセージを実装しています。

それでは、実装箇所を一つ一つ見ていきます。

#### 解説
3行目でflashメッセージの親要素となるdivタグを定義します。

```application.html.haml
  .notifications
```

4行目でflashオブジェクトを呼び出しています。
投稿成功の場合、この時点で、keyに"notice"、valueに"メッセージが送信されました"が入ります。

```application.html.haml
  .notifications
    - flash.each do |key, value|
```
5行目でcontent_tagを使用してflashメッセージを表示させるためのdivタグを作成します。前述の通り、keyに"notice"、valueに"メッセージが送信されました"が入っていますので、classが"notice"、表示させたい内容が"メッセージが送信されました"となります。

```application.html.haml
  .notifications
    - flash.each do |key, value|
      = content_tag(:div, value, class: key)
```


ちなみに、content_tagはHTMLタグを生成するメソッドで、以下のように使います。

```
content_tag(作成するタグの種類△表示したい内容△class等のオプション）
※△は半角スペース
```



### 3. CSSを追加する
上記のviewの実装にて追加したクラスにCSSを追加します。
ここは自由にCSSをあててください。一応、以下は例です。

```application.scss
.notifications {
  color: $white;
  text-align: center;  

  .notice {
    background-color: lightBlue;
  }

  .alert {
    background-color: alert_orange;
  }
}

```

以上で、flashメッセージを表示させることが出きます。

## flashとflash.nowの使い分け方は？
結論から言うと、以下のように使い分けます。
**　　・flash: 　　　リダイレクトする時
　　  ・flash.now:　renderする時**

なぜか？
それを理解するためには、以下の２つを理解する必要があります。

- リダイレクトとrenderの挙動の違い
- flashの特徴

### リダイレクトとrenderの挙動の違い
こちらについては、以下の記事がわかりやすかったです。

参考：[【Rails】renderとredirect_toの違いと使い分け]
(https://qiita.com/morikuma709/items/e9146465df2d8a094d78)

一部抜粋せていただくと、、、"今回は"以下のような流れで次の画面を描写します。
>・render　　　　　：　controller(createアクション)　→　view(index.html)
>・redirect_to　　　：　controller(createアクション)　→　URL　→　route　→　controller(indexアクション)　→　view(index.html)

リダイレクトの場合、createアクションの直後にrouteを経由して次のindexアクションが実行される事がわかります。
言い換えれば、createアクションとindexアクションは別のリクエストとなります。
一方renderでは、createアクションの直後にindex.htmlを表示しています。
こちらは同じリクエストとなっています。

これを踏まえてflashの特徴を見ていきます。

### flashとflash.nowの特徴について

■flashの特徴
flashについて、Railsのドキュメントには以下の様に記載されています。
>flashはセッションの中の特殊な部分であり、*<font color="Red">リクエストごとにクリアされます。つまりflashは「直後のリクエスト」でのみ参照可能になる</font>*という特徴を持ち、エラーメッセージをビューに渡したりするのに便利です。

■flash.nowの特徴
flash.nowについて、Railsのドキュメントには以下の様に記載されています。

>デフォルトでは、flashに値を追加すると直後のリクエストでその値を利用できますが、次のリクエストを待たずに同じリクエスト内でこれらのflash値にアクセスしたい場合があります。たとえば、createアクションに失敗してリソースが保存されなかった場合に、newテンプレートを直接描画するとします。このとき新しいリクエストは行われませんが、この状態でもflashを使ってメッセージを表示したいこともあります。このような場合、flash.nowを使えば通常のflashと同じ要領でメッセージを表示できます。

つまり今回で言うと、、、
メッセージ投稿成功時はリダイレクトしており、<font color="Red">直後のリクエスト後(indexアクション後)</font>にflashメッセージが参照可能となっているため、flashを使用しています。
また、メッセージ投稿失敗時には、<font color="Red">同じリクエスト内</font>でflashメッセージを表示したいので、flash.nowを使用しています。

参考：[Ruby on Railsドキュメント]
(http://railsdoc.com/references/flash)

## まとめ
備忘として、flashメッセージの実装方法について纏めました。
理解不足により、認識が間違っている点や不明点等あればご指摘下さい。