Rails で bootstrap を使うことになったので、せっかくだから更新成功時に Toasts を表示しよう考えました。
Bootstrap の Toasts は自分で初期化する必要があるのですが、window の load イベントを契機に初期化しようとして失敗しました。
というのも Rails には Turbolinks という機能があって、History API を使って遷移を行うために、ページ遷移時に window の load イベントが発火しないのです。
Toasts の初期化の仕方や show の呼び方をいろいろ試した結果、とりあえず表示するところまではたどり着けたので、備忘録代わりに記録を残します(もっと上手いやり方があったら知りたい)。
Docker で環境を作っているので、そこらへんは適宜読み替えてください。
環境
- Ruby 3.1.1
- Ruby on Rails 7.0.2
- Docker 20.10.10, build b485636
- Docker Compose v2.2.3
- node.js v16.14.0
- bootstrap 5
インストール
https://qiita.com/mkt1234/items/b852eeeb213e46702236 にある方法でインストールしました。
Rails をインストールするときのオプションに -css=bootstrap
を追加しました(Bootstrap のインストールはこれが一番楽だと思う)。
docker compose run --rm --no-deps web rails new . --force --css=bootstrap -d=postgresql
ひな形を作成
まずは scaffold で User を作成します。
docker compose run --rm web rails g scaffold User name:string
Bootstrap Toasts の作成
各ページに Toasts を埋め込むのは大変なので、body の直下に 1 つだけ埋め込んで、render の notice で制御するようにしました。
notice と flash の使い分けが良く分からなかったので notice を使っています。
Toasts 用の erb を作成
notice の有無で data-controller 属性の表示を切り替えるようにしました(他に良いアイデアが思い浮かばなかった……)。
この data-controller 属性がポイントのようです。
position-fixed start-50 translate-middle-x
は位置調整なのでお好みで。
<div class="toast position-fixed start-50 translate-middle-x"
role="alert" aria-live="assertive" aria-atomic="true"
<%= !!notice && "data-controller=toast" %>>
<div class="toast-header">
<strong class="me-auto">成功</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">更新成功</div>
</div>
Toasts を body の下に配置
body の下に _toast.html.erb を配置します。
...
<body>
<%= render "common/toast" %>
<%= yield %>
</body>
...
既存の notice の表示を削除
notice の文言は Toasts に置き換えるので、各 erb の先頭にある <p style="color: green"><%= notice %></p>
を消します(index.html.erbの先頭のやつも消しておく)。
<%= render @user %>
<div>
<%= link_to "Edit this user", edit_user_path(@user) %> |
<%= link_to "Back to users", users_path %>
<%= button_to "Destroy this user", @user, method: :delete %>
</div>
Stimulus の設定
Stimulus の Controller を作ります(言い方あっているのか?)。
ここが一番の苦労したポイント。
data-controller="toast" を指定した要素がロードされた瞬間に、この controller(toast_controller.js) の connect()
が呼ばれるので、そこで Toasts を初期化します。
初期化したらすぐに .show()
を呼んで Toasts を表示させます。
import { Controller } from '@hotwired/stimulus'
import * as bootstrap from 'bootstrap'
export default class extends Controller {
connect() {
new bootstrap.Toast(this.element, { delay: 2000 }).show()
}
}
toast_controller を読み込みます。
import { application } from './application'
import ToastController from './toast_controller'
application.register('toast', ToastController)
precompile
app/javascript 以下のファイルを precompile します。
自分で package.json に sciprts.build などを作っていれば docker compose run --rm web yarn build
の方が速いです。
docker compose run --rm web rails assets:precompile
表示
作成・更新成功時に Toasts が表示できるようになりました。
あとは自分好みに Toasts のスタイルを変えるだけ。