RailsでjQueryを読み込まずにmethod: :deleteのリンクを実現する(Routing Error No route matches [GET] ... の対処法)

jQueryを読み込まずにRailsで開発している場合、deviseのログアウト処理など、method: :deleteを使っている時に困ることがあります。

<%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>

このように書いていてもget処理にしかならず、routingエラーが出てしまいます。

Routing Error No route matches [GET] ...

原因はざっくり説明するとjQueryを読み込んでいないからで、下記参考記事によるとjquery-ujsを読み込んでいる時はjquery-ujsrails.jsがうまいこと動作させてくれているようです。

jquery-ujs/src/rails.js
    // Handles "data-method" on links such as:
    // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
    handleMethod: function(link) {
      var href = rails.href(link),
        method = link.data('method'),
        target = link.attr('target'),
        csrfToken = rails.csrfToken(),
        csrfParam = rails.csrfParam(),
        form = $('<form method="post" action="' + href + '"></form>'),
        metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';

      if (csrfParam !== undefined && csrfToken !== undefined && !rails.isCrossDomain(href)) {
        metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
      }

      if (target) { form.attr('target', target); }

      form.hide().append(metadataInput).appendTo('body');
      form.submit();
    },

https://github.com/rails/jquery-ujs/blob/9e805c90c8/src/rails.js#L213-L232

この問題の解決策は

  • get処理をするようにroute.rbに書く
  • deviseの設定を書き換え、getをdeleteにする

などがよく挙げられていたのですが、あまり気持ちよくないので別の方法がないか調べていました。

config/initializers/devise.rbでdeleteをgetにする
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
  ...
  # The default HTTP method used to sign out a resource. Default is :delete.
  config.sign_out_via = :delete # ここをgetにする
  ...
end

すると、良さそうな方法が見つかったのでご紹介しようと思います。

button_toを使う

link_tobutton_toに書き換えるだけで、一応deleteとして動くようになります。

<%= button_to 'ログアウト', destroy_user_session_path, method: :delete %>
HTML出力結果
<form class="button_to" method="post" action="/users/sign_out">
  <input type="hidden" name="_method" value="delete">
  <input type="submit" value="ログアウト">
  <input type="hidden" name="authenticity_token" value="(省略)">
</form>

confirmに対応させる

ただ、これは確認ダイアログを出す処理には対応していません(押してもconfirmが出ない)。これだと、削除処理を行うときは少し心もとないので、以下の様なjsを追加してやります。

window.onload = () => {

  class Confirm {
    constructor(el) {
      this.message = el.getAttribute('data-confirm')
      if (this.message) {
        el.form.addEventListener('submit', this.confirm.bind(this))
      } else {
        console && console.warn('No value specified in `data-confirm`', el)
      }
    }

    confirm(e) {
      if (!window.confirm(this.message)) {
        e.preventDefault();
      }
    }
  }

  Array.from(document.querySelectorAll('[data-confirm]')).forEach((el) => {
    new Confirm(el)
  })

}

これでconfirmも動くようになり、削除などを伴う処理でもいい感じに処理ができるようになりました。

<%= button_to 'ログアウト', destroy_user_session_path, method: :delete, data: { confirm: 'ログアウトしてもよろしいですか?' } %>

参考記事

y-temp4
フリーランスエンジニア。Webのフロントエンドが好きです。お仕事のご相談はTwitterのDMまで!
https://y-temp4.com
admin-guild
「Webサービスの運営に必要なあらゆる知見」を共有できる場として作られた、運営者のためのコミュニティです。
https://admin-guild.slack.com
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント
この記事にコメントはありません。
あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした