Turbo Stream の flash メッセージを layout で共通化する

## 結論
`views/layouts/application.turbo_stream.erb` と `views/layouts/turbo_rails/frame.turbo_stream.erb` に書くのがよさそう。

## 解説
Rails では layouts/application.html.erb に flash メッセージの表示処理を書いておくのが一般的で、turbo_stream でも layouts/application.turbo_stream.erb を用いてメッセージの表示処理を共通化することができます。しかし、turbo_frame 内からリクエストされた場合は、この layout が適用されず、flash メッセージを出力するには別の工夫が必要でした。

これは turbo_frame リクエストのときは `layout: false` となるようになっていたためですが、turbo-rails v1.4.0 で以下のように変更されました。

```diff_ruby
  included do
-   layout -> { false if turbo_frame_request? }
+   layout -> { "turbo_rails/frame" if turbo_frame_request? }
    etag { :frame if turbo_frame_request? }
  end
```

https://github.com/hotwired/turbo-rails/pull/428

このとき、デフォルトの layout ファイルは以下のように適用されます。

* format.html => application.html.erb
* format.turbo_stream => application.turbo_stream.erb
* format.html + turbo_frame request => turbo_rails/frame.html.erb
* format.turbo_stream + turbo_frame request　=> turbo_rails/frame.turbo_stream.erb

つまり turbo-rails v1.4.0 以降では、`application.turbo_stream.erb` と `turbo/frame.turbo_stream.erb` を追加し、この2つのファイルに flash メッセージの出力処理 (turbo_stream.update など) を記載しておけばよい、ということになります。

どうしても turbo-rails v1.4.0 より前のバージョンを利用したい場合は、カスタムレイアウトを用意しておくとよさそうです。

https://github.com/hotwired/turbo-rails#a-note-on-custom-layouts


## サンプルコード

### 前提
flash メッセージを表示するには、`id="flash_messages"` の要素にメッセージを含む要素を追加するものとします。また、この記事のサンプルコードは CSS を省きます。

```html
<div id="flash_messages">
  <p>処理成功のメッセージなど</p>
</div>
```

### flash メッセージを表示するための partial view

```erb:app/views/application/_turbo_stream_flash.erb
<% if notice || alert %>
  <%= turbo_stream.update 'flash_messages' do %>
    <% if notice %>
      <p><%= notice %></p>
    <% end %>
    <% if alert %>
      <p><%= alert %></p>
    <% end %>
  <% end %>
<% end %>
```

### turbo_stream のレイアウトファイル

turbo_stream で、かつ **turbo_frame_request ではないとき**に render されるレイアウト

```erb:app/views/layouts/application.turbo_stream.erb
<%= render 'turbo_stream_flash' %>
<%= yield %>
```

turbo_stream で、かつ **turbo_frame_request のとき**に render されるレイアウト

```erb:app/views/layouts/turbo_rails/frame.turbo_stream.erb
<%= render 'turbo_stream_flash' %>
<%= yield %>
```

まったく同じ内容のファイルなので `application.turbo_stream.erb` のみにまとめたいところですが、カスタムレイアウトのメソッドを用意して turbo_stream の分岐をいれて〜と処理を足すより、これくらいのコード量であればファイルを2つ用意した方がよいと考えています。

### コントローラ／アクションメソッドの例

```ruby
def create
  @some_model = SomeModel.new(some_model_params)
  if @some_model.save
    flash.now.notice = '登録しました。'
    # turbo_stream でページ更新する
  else
    render :new, status: :unprocessable_entity
  end
end

def update
  if @some_model.update(some_model_params)
    flash.now.notice = '更新しました。'
    # turbo_stream でページ更新する
  else
    render :edit, status: :unprocessable_entity
  end
end
```

## 参考

「猫でもわかるHotwire入門 Turbo編」に、turbo_stream における　flash メッセージの表示や stimulus による　toast　化など、すごく詳しい解説が載っています。とても参考になります。

https://zenn.dev/shita1112/books/cat-hotwire-turbo/viewer/tutorial-2#flash%E3%81%AEturbo-streams%E5%8C%96

https://zenn.dev/shita1112/books/cat-hotwire-turbo/viewer/tutorial-3#flash%E3%81%ABtoast%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B


## 告知

**hotwire.love** という hotwire の勉強会を毎月第２/第4木曜に開催しています。hotwire に関する情報共有の場で、ゆるくおしゃべりをしたり、モブプロで実際の動きを試してみたりしています。興味のあるかたは是非遊びに来てください。

https://hotwire-love.connpass.com/