😎

【Rais】DeviseでログアウトがGETメソッドになるエラーの解決方法

2023/08/24に公開

今日は簡単にシェアします。
この問題については、多くの人が記事を書いており、そのトリガーは人によって異なるようです。そこで、今回は俺の経験を元に記事を書いてみました。

追記

以下記事のコードを消してもまた再発しました。
他の記事を参考にしたところルーティングに以下を追記すると解決しました。

routes.rb
  devise_scope :user do
    get '/users/sign_out' => 'devise/sessions#destroy'
  end

エラー内容

link_toを使ってログアウトボタンを作った際、期待通りにDELETEメソッドが使用されないため、ログアウトが正しくできません。

<%= link_to destroy_user_session_path, method: :delete do %><i class="fas fa-sign-out-alt"></i>ログアウト<% end %>

DELETEメソッドになるはずなのに、なぜかGETメソッドが使用されてしまいます。

原因

この問題を追究した結果、JavaScriptのコードが影響していることが分かりました。具体的には、ドロップダウンメニューをJavaScriptで作成し、その中にログアウトボタンがある状況で発生していました。ドロップダウンメニューのコードは以下の通りです。

問題のコード

具体的には、以下のJavaScriptコードの一部が原因でした。

:
<%= link_to destroy_user_session_path, method: :delete do %><i class="fas fa-sign-out-alt"></i>ログアウト<% end %>
:

<script>
document.addEventListener('turbolinks:load', function() {
  const mypageBtnContainer = document.getElementById('mypageBtnContainer');
  const mypageDropdown = document.getElementById('mypageDropdown');

  function toggleDropdown() {
    $(mypageDropdown).slideToggle(300);
  }

  if (mypageBtnContainer) {
    mypageBtnContainer.addEventListener('click', toggleDropdown);

    document.addEventListener('turbolinks:before-visit', function() {
      $(mypageDropdown).slideUp(300);
    });

    // ドロップダウンメニュー内部のクリックイベントをキャンセル
    // mypageDropdown.addEventListener('click', function(event) {
    //   event.stopPropagation();
    // });

    document.addEventListener('click', function(event) {
      if (!mypageBtnContainer.contains(event.target)) {
        mypageDropdown.style.display = 'none';
      }
    });
  }
});
</script>

コメントアウトしているevent.stopPropagation();がトリガーになっていました。

`event.stopPropagation()` メソッドとは

event.stopPropagation() メソッドは、イベントの伝播(イベントバブリング)を中断するためのものです。イベントバブリングとは、要素でイベントが発生した際に、そのイベントが親要素や上位の要素にも伝わっていく仕組みを指します。

これだとわかりにくいので、、、今回の意図を説明します。

このコードの場合、アイコンをクリックするとドロップダウンメニューが開き、アイコンやドロップダウンメニュー外の部分をクリックするとメニューが閉じるようになっています。ただし、ドロップダウンメニュー自体をクリックした場合にはメニューを閉じたくなかったので、そのために event.stopPropagation() を使っています。

これにより、ドロップダウンメニュー内部でクリックイベントが発生しても、そのイベントが親要素に伝わるのを防ぐことができ、ドロップダウンメニューをクリックしてもメニューが閉じることがありません。メニュー内部でクリックしても、メニューは開いたままとなります。

解決策

問題の解決策としては、シンプルに問題のコードを削除することで解決しました。
消しても使い勝手はそこまで変わらないので。

ちなみにlink_toの問題を避けるために、代わりにbutton_toを使うこともできます。

<%= button_to destroy_user_session_path, method: :delete %>

解決策は人によって異なる内容の記事を書かれているので、色々な記事を参考にしていただけたらと思います。
なんかすっきりしないエラーですよね。

Discussion