【Rails】data-disable-withって何者

## 結論
HTMLのフォーム送信ボタンに使用される、
**二重サブミットを防止**する便利なオプションです。


本記事では、
- data-disable-withの挙動
- data-disable-withが使えないケース
- data-disable-withが使えない時の代替案

をざっくりと説明していきたいと思います。

## data-disable-withの挙動
はじめに、data-disable-withとは
- Railsの`rails-ujs`[^1]というJavaScriptライブラリによって提供される
- フォーム送信ボタンにオプションとして指定することが出来る
- 指定されたボタンは、一度クリックされるとボタンが無効となる

といった特徴から、
railsでフォーム送信を行う際の二重サブミット[^2]対策として使われています。

以下はform_withにdata-disable-with属性を使用する際のサンプルコードです。

[^1]: **UJS**とは「Unobtrusive（控えめな）JavaScript」の略で、
rails-ujsとはrailsでJavaScriptの処理を簡単に行えるようにする機能のこと。
[^2]: フォーム送信後の通信している間、送信ボタンが有効であることで、複数回送信してしまうこと。これにより注文の重複やフォームの意図しない誤送信が起こる。

```erb:sample.html
<%= form_with url: '/your_url', method: :post do |form| %>
  <%= 中略 %>
  <%= form.submit 'Submit', data: { disable_with: 'Submitting...' } %>
<% end %>
```

上記のように書くことで、サブミット送信後、
ローディング中に指定した「Submitting...」という文言が送信ボタンに表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348975/8fb153e9-af77-175e-360f-1f412a5c43ac.png)
これを押すと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348975/c4e5bfd6-1680-3655-0825-5d3d024a92c2.png)
こうなる

「Submitting...」という文言が表示されている間は、ボタンは無効になります。
そのため、複数回連続で送信を押す二重サブミットを防ぐことが出来ます。

## data-disable-withが使えないケース
以下に、data-disable-withが使えないときに考えられる原因をいくつか紹介します。

- そもそもrails-ujsをインストールしていない
- 他の外部ライブラリとの兼ね合い
- 他のJSとの兼ね合い
- data-disable-withつきの送信ボタンが、同じ画面上に複数個ある

本記事では、上記の理由の中から私が実際に直面した
「**そもそもrails-ujsをインストールしていない**」「**他の外部ライブラリとの兼ね合い**」
の2つを詳しく解説します。

### そもそもrails-ujsをインストールしていない
rails-ujsというのは、rails5.0~6.1での標準ライブラリです。

しかし、私が使用していたrails7.0では、
rails-ujsは**標準ライブラリから除外**されていたため、
上記サンプルコードのように記述しても動作しませんでした。

そのため、rails5.0~6.1以外のバージョンでrails-ujsを使用したい場合は、
```ruby
gem 'rails-ujs'
```
というように、明示的にライブラリをインストールする必要があります。

### 他の外部ライブラリとの兼ね合い
私が使用しているrails7.0には、標準ライブラリとしてturbo, stimulusが採用されています。
特にturboは、**デフォルトでajaxのフォーム送信を操作するライブラリ**のため、
**rails-ujsの操作とバッティングしてうまく動作しない**場合が出てきます。

結論、rails7.0でdata-disable-withの機能のためだけに（あるいはそれをきっかけにして）、
あえてrails-ujsを入れるのはオススメしません。
では、代わりにどのようなコードで二重サブミットを防ぐようにするべきでしょうか。

## data-disable-withが使えない時の代替案
今回はJavaScriptのonClick処理を使い、
サブミットボタン押下時にdisable属性を付与するやり方を取りました。

```erb:alter_sample.html
<%= form_with url: '/your_url', method: :post do |form| %>
　　　　<%= 中略 %>
  <%= form.button 'Submit', type: 'button', onclick: "clickEvent(event);" %>
<% end %>

<script>
    function clickEvent(e) {
        e.target.disabled = true;
        e.target.value = 'Submitting...';

        const form = e.target.closest('form');
        form.submit();
    }
</script>
```

これにより、
- 送信後、ボタン操作が無効となる
- ボタンに「Submitting...」と表示される
- 上記の処理を行った上でサブミットする

という要件を満たしたJSの操作が実現しました。

フォーム送信には欠かせない二重サブミット対策ですが、
data-disable-withを使用する際はまず、
**railsのバージョン**と**rails-ujsインストールの有無**を確認してみてください！

### 追記(2024/06/14)：rails7.0でdata-disable-withを実現する

rails7.0に伴い導入されたturboの仕組みを使って、
より簡単にdata-disable-withを実現する方法がありましたので紹介します。
これは、turboの処理とCSSを組み合わせることで実現可能です。

```erb:turbo_sample.html
<%= form_with url: '/your_url', method: :post do |form| %>
　　　　<%= 中略 %>
  <%= form.button do %>
    <span class="show-when-enabled">Submit</span>
    <span class="show-when-disabled">Submitting...</span>
  <% end %>
<% end %>
```
```scss:turbo_sample.scss
button {
    .show-when-enabled { display: initial; }
    .show-when-disabled { display: none; }

    &[disabled] {
        .show-when-enabled { display: none; }
        .show-when-disabled { display: initial; }
    }
}
```

rails7.0を使っている人はこちらも参考にしてみてください！

## 参考記事
[Railsガイド「3.4 入力を自動で無効にする」](https://railsguides.jp/v6.1/working_with_javascript_in_rails.html#:~:text=%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84%E3%80%82-,3.4%20%E5%85%A5%E5%8A%9B%E3%82%92%E8%87%AA%E5%8B%95%E3%81%A7%E7%84%A1%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B,-data%2Ddisable%2Dwith)
[ザリガニが見ていた...。「:disable_withで処理中を明確にして二重送信を防止」](https://zariganitosh.hatenablog.jp/entry/20070623/1182551690)
[Qiita「同じform内にsubmitボタンが複数ある際のdisable_withの使い方」](https://qiita.com/nowin/items/f5bebda6bb28b3bcdb85)
[ボクココ「rails-ujs と form_with の使い方」](https://www.bokukoko.info/entry/2017/12/22/194327)
[GoRails（youtube動画）「Migrating from Rails UJS to Hotwire: Data Confirm, Method, and Disable With」](https://www.youtube.com/watch?v=J1GAYLQvtBU&t=26s)