LoginSignup
9
2

More than 1 year has passed since last update.

data-turbo-confirm の確認ダイアログをカスタマイズする方法

Last updated at Posted at 2023-01-30

完成イメージ

turbo-confirm.gif

はじめに

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 で読み取るためです。

app/views/layouts/application.html.erb
<!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 の値を代入してくれます。

app/javascript/lib/bootstrap_confirm.js
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' を追記します。

app/javascript/application.js
// 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 のモーダルを使うことができます。

turbo-confirm.gif

さらにカスタマイズ

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: '本当によろしいですか?' } } %>
app/javascript/lib/bootstrap_confirm.js
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' } } %>
app/javascript/lib/bootstrap_confirm.js
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)

とすると、モーダルのタイトルとボタンのテキストと色をフォームによって変更できます。

turbo-confirm-dynamic.gif

参考リンク

GoRails

dialog タグを使って、確認ダイアログをカスタマイズする方法が紹介されています。

動画中のコード

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2