Edited at

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

More than 3 years have passed since last update.

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/master/src/rails.js#L210#L229

この問題の解決策は


  • 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: 'ログアウトしてもよろしいですか?' } %>


参考記事