LoginSignup
7
12

More than 5 years have passed since last update.

Railsで、deviseのログインフォームをBootstrapを利用してモーダル化した手順

Last updated at Posted at 2018-10-08

現在の状態

rails初心者。
Rails5のサンプルアプリにdeviseでログイン機能を追加したところ。
ヘッダーメニューにログインページへのリンクを配置している。

やりたいこと

bootstrap4のモーダルウインドウを利用して、ログインフォームをモーダル化したい。
ログイン後は、特定のページに飛ぶのではなくログイン前に見ていたページに戻るようにしたい。

準備

  • Bootstrap4の導入
  • ターミナルにrails g devise:controllers usersコマンドで、/controller/users以下にdeviseのコントローラーファイルを生成する。

実行

リンクを修正

  • ログインフォームへのリンクをモーダル呼び出しボタンにするため、data-toggledata-targetを追加する。
application.html.erb
<%= link_to "ログイン", '', class: "nav-link", "data-toggle": "modal", "data-target": "#signinModal" %>

/sessions/new.html.erbのログインフォーム部分をモーダルの中(<div class="modal-body">以下)に貼り付ける。

application.html.erb
<div class="modal fade" id="signinModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" style="margin: 200px auto; height: 700px;">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">サンプル</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">

        <!-- ここからフォーム -->
        <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
          <div class="field">
            <%= f.label :email %><br />
            <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
          </div>

          <div class="field">
            <%= f.label :password %><br />
            <%= f.password_field :password, autocomplete: "current-password" %>
          </div>

          <% if devise_mapping.rememberable? -%>
            <div class="field">
              <%= f.check_box :remember_me %>
              <%= f.label :remember_me %>
            </div>
          <% end %>

          <div class="actions">
            <%= f.submit "ログイン" %>
          </div>
        <% end %>

      </div>
    </div>
  </div>
</div>

ログインに成功したら、ログイン前に見ていたページにリダイレクトするようにする。

application_controller.rb

class ApplicationController < ActionController::Base
  before_action :store_user_location!, if: :storable_location?
  before_action :authenticate_user!

  private
    def storable_location?
      request.get? && is_navigational_format? && !devise_controller? && !request.xhr? 
    end

    def store_user_location!
      store_location_for(:user, request.fullpath)
    end

    def after_sign_in_path_for(resource_or_scope)
      stored_location_for(resource_or_scope) || super
    end
end

これで、ログインフォームのモーダル化、ログイン後の元のページへのリダイレクトが設定できた。

ログインに失敗しても、元々見ていたページにリダイレクトするようにする。

  • 今の状態だと、ログインに失敗したら/sessions/new.html.erbに飛んでしまうのでログイン失敗時のリダイレクト先も設定する。
  • form_for内にf.hidden_fieldを追加し、現在開いているページのURLを取得して:urlという名前でcontrollerに渡す。
application.html.erb
 ()

<%# ログイン失敗時にこのページに戻れるように、:urlという名前でURLを一緒に送る %>
<%= f.hidden_field :url, value: request.fullpath %>

 (略)
sessions_controller.rb
     ()

# ログイン失敗後は def failed に飛ぶように変更
  def create
    auth_options = { scope: resource_name, recall: "#{controller_path}#failed" }
    self.resource = warden.authenticate!(auth_options)
    set_flash_message!(:notice, :signed_in)
    sign_in(resource_name, resource)
    yield resource if block_given?
    respond_with resource, location: after_sign_in_path_for(resource)
  end

# ログイン失敗の時は直前のURLにリダイレクトする
  def failed
    flash[:alert] = "メールアドレスまたはパスワードが違います。"
    redirect_to params[:user][:url]
  end
    ()
  • def createの1行目auth_optionsrecallで、ログインに失敗したときのアクションを指定する。通常はsessions#newに飛ぶようになっているが、ここではfailedアクションを呼び出す様にしている。
  • その下にfailedアクションを定義している。ログインフォームから渡されたURLをparams[:user][:url]で取得し、リダイレクト先とする。

  • 最後に、routes.rbにて、カスタマイズしたsessions_controller.rbを使えるようにする。

routes.rb
devise_for :users, controllers: { sessions: 'users/sessions'}

これでログインが成功しても失敗しても、元のページに戻れるようになった!

その他、途中でひっかかったこと

  • ヘッダーメニューからログインフォームを呼び出すので、link_toの直後にモーダル本体の処理を書いていたところ、モーダルが影の下に隠れてしまい困った。ヘッダーメニューを上部固定にしていたのだが、position: absolute;な要素内にモーダルを配置するとこういうことが起きるらしい。モーダル本体をヘッダーメニューの外に出したら解決した。
    http://develop-hish-1ne.hatenablog.com/entry/2014/01/31/163704

  • view/devise/session/new.html.erbの中身をモーダルの中に移したところ、undefined local variable or method 'resource_name'とエラーが出た。application_helperに以下の内容を記述したら解決した。
    ↓こちらの記事の通りにさせていただいただきました。
    http://hajimete-ruby.jugem.jp/?eid=84

application_helper.rb
def resource_name
   :user
end

def resource
   @resource ||= User.new
end

def devise_mapping
   @devise_mapping ||= Devise.mappings[:user]
end

モデル名がUserではない場合は実際のモデル名に合わせる。

  • なんだか無駄に遠回りしているような気がしますが、もっと簡単なやり方などあれば教えていただけると幸いです...。

参考にさせたいただいたページ

7
12
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
7
12