完成イメージ
はじめに
Turbo では、フォームやリンクに data-turbo-confirm
属性をつけると確認ダイアログを表示できます。
通常はブラウザ標準の確認ダイアログ window.confirm()
が使われます。
実は、Turbo.setConfirmMethod()
で window.confirm()
ではなく別の処理を実行するように設定できます。
適当な関数を Turbo.setConfirmMethod()
の引数にすることで、window.confirm()
の代わりに独自の確認ダイアログを表示できるようになります。
引数にする関数が Promise<boolean>
を返すのであれば、dialog タグを使うことも、Bootstrap のような CSSフレームワークのモーダルを使うことも、Micromodal.js のようなライブラリを使うこともできます。
Turbo のバージョンは 7.2.0 以上である必要があります。
非同期な処理が必要なためです。
https://github.com/hotwired/turbo/releases/tag/v7.2.0
https://github.com/hotwired/turbo/pull/525
この記事では具体例として、Bootstrap のモーダルを確認ダイアログとして使う方法を紹介します。
カスタマイズ例( Bootstrap のモーダルを使う場合)
環境
フレームワーク、ライブラリなど | バージョン |
---|---|
Rails | 7.0.3.1 |
Turbo | 7.2.2 |
Bootstrap | 5.2.0 |
フォームに data-turbo-confirm
属性
確認ダイアログを出したいフォームやリンクに data-turbo-confirm
属性を追記します。
<%= button_to '削除', post_path(@post), method: :delete, class: 'btn btn-outline-danger', form: { data: { turbo_confirm: '本当によろしいですか?' } } %>
レイアウトファイルにモーダルの HTML
app/views/layouts/application.html.erb にモーダルの HTML を追記します。
モーダルの要素、メッセージの要素、承認ボタンに id 属性をつけています。
JavaScript で読み取るためです。
<!DOCTYPE html>
<html>
<!-- ... -->
<body>
<%= yield %>
<div class="modal fade" id="turbo-confirm-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body" id="turbo-confirm-modal-message"></div>
<div class="modal-footer">
<button class="btn btn-light" data-bs-dismiss="modal">キャンセル</button>
<button class="btn btn-primary" id="turbo-confirm-modal-confirm-button">OK</button>
</div>
</div>
</div>
</div>
</body>
</html>
モーダルを表示する関数を定義し、Turbo.setConfirmMethod()
の引数に
app/javascript/lib/bootstrap_confirm.js では、bootstrapConfirm()
を定義し、Turbo.setConfirmMethod()
の引数にしています。
lib/bootstrap_confirm.js というファイル名と bootstrapConfirm
という関数名は好きに変えても問題ないです。
bootstrapConfirm()
は、
- モーダルのメッセージを変更
- モーダルを表示
- 承認ボタンがクリックされたときに
Promise.resolve(true)
を返す
ということを行っています。
message
には Turbo が data-turbo-confirm
の値を代入してくれます。
import { Modal } from 'bootstrap'
const bootstrapConfirm = (message) => {
const modalElement = document.getElementById('turbo-confirm-modal')
const messageElement = document.getElementById('turbo-confirm-modal-message')
const confirmButtonElement = document.getElementById('turbo-confirm-modal-confirm-button')
const modal = new Modal(modalElement)
messageElement.textContent = message
modal.show()
return new Promise((resolve) => {
confirmButtonElement.addEventListener(
'click',
() => {
resolve(true)
modal.hide()
},
{ once: true },
)
})
}
Turbo.setConfirmMethod(bootstrapConfirm)
エントリーポイントで読み込む
エントリーポイントの app/javascript/application.js に import './lib/bootstrap_confirm'
を追記します。
// Entry point for the build script in your package.json
import '@hotwired/turbo-rails'
import './controllers'
import 'bootstrap'
import './lib/bootstrap_confirm'
これで data-turbo-confirm
の確認ダイアログで Bootstrap のモーダルを使うことができます。
さらにカスタマイズ
bootstrapConfirm()
の引数には最大で3つまで渡すことができます。Turbo によって、順に
-
data-turbo-confirm
の値 -
data-turbo-confirm
がついているフォーム要素 -
data-turbo-confirm
がついているフォーム要素のサブミットボタン
が代入されます。
例えば、コードを
<%= button_to '削除', post_path(@post), method: :delete, class: 'btn btn-outline-danger', form: { data: { turbo_confirm: '本当によろしいですか?' } } %>
const bootstrapConfirm = (message, formElement, submitter) => {
console.log(message)
console.log(formElement)
console.log(submitter)
}
Turbo.setConfirmMethod(bootstrapConfirm)
とすれば、message
, formElement
, submitter
は
<!-- console.log(message) -->
本当によろしいですか?
<!-- console.log(formElement) -->
<form data-turbo-confirm="本当によろしいですか?" class="button_to" method="post" action="/posts/1">
<input type="hidden" name="_method" value="delete" autocomplete="off">
<button class="btn btn-outline-danger" type="submit">削除</button>
<input type="hidden" name="authenticity_token" value="Zhb-6EgmOLl9puHT" autocomplete="off">
</form>
<!-- console.log(submitter) -->
<button class="btn btn-outline-danger" type="submit">削除</button>
となります。
このことを利用すると、formElement
から取得できるフォームに data-ooo
をつけて、モーダルをフォームによって少し変化させることもできると思います。
<%= button_to '更新', post_path(@post), class: 'btn btn-primary', form: { data: { turbo_confirm: '本当に更新しますか?', turbo_confirm_title: '投稿の編集', turbo_confirm_button_class_list: 'btn btn-primary' } } %>
<%= button_to '削除', post_path(@post), method: :delete, class: 'btn btn-outline-danger', form: { data: { turbo_confirm: '本当に削除しますか?', turbo_confirm_title: '投稿の削除', turbo_confirm_button_class_list: 'btn btn-danger' } } %>
import { Modal } from 'bootstrap'
const bootstrapConfirm = (message, formElement, submitter) => {
// ...
const titleElement = document.getElementById('turbo-confirm-modal-title')
const messageElement = document.getElementById('turbo-confirm-modal-message')
const confirmButtonElement = document.getElementById('turbo-confirm-modal-confirm-button')
// ...
titleElement.textContent = formElement.getAttribute('data-turbo-confirm-title')
messageElement.textContent = message
confirmButtonElement.classList = formElement.getAttribute('data-turbo-confirm-button-class-list')
confirmButtonElement.textContent = submitter.textContent
modal.show()
// ...
}
Turbo.setConfirmMethod(bootstrapConfirm)
とすると、モーダルのタイトルとボタンのテキストと色をフォームによって変更できます。
参考リンク
GoRails
dialog タグを使って、確認ダイアログをカスタマイズする方法が紹介されています。
動画中のコード