##現在の状態
rails初心者。
Rails5のサンプルアプリにdeviseでログイン機能を追加したところ。
ヘッダーメニューにログインページへのリンクを配置している。
##やりたいこと
bootstrap4のモーダルウインドウを利用して、ログインフォームをモーダル化したい。
ログイン後は、特定のページに飛ぶのではなくログイン前に見ていたページに戻るようにしたい。
##準備
- Bootstrap4の導入
- ターミナルに
rails g devise:controllers users
コマンドで、/controller/users
以下にdeviseのコントローラーファイルを生成する。
##実行
####リンクを修正
- ログインフォームへのリンクをモーダル呼び出しボタンにするため、
data-toggle
とdata-target
を追加する。
<%= link_to "ログイン", '', class: "nav-link", "data-toggle": "modal", "data-target": "#signinModal" %>
####/sessions/new.html.erb
のログインフォーム部分をモーダルの中(<div class="modal-body">
以下)に貼り付ける。
<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">×</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
を修正する。といっても貼り付けるだけ。
https://github.com/plataformatec/devise/wiki/How-To:-Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update
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
に渡す。
(略)
<%# ログイン失敗時にこのページに戻れるように、:urlという名前でURLを一緒に送る %>
<%= f.hidden_field :url, value: request.fullpath %>
(略)
-
次は
sessions_controller
に、ログイン失敗後の処理を追加する。↓を参考に。
https://github.com/plataformatec/devise/blob/master/app/controllers/devise/sessions_controller.rb -
sessions_controller
のcreate
メソッドを以下のように修正。
(略)
# ログイン失敗後は 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_options
のrecall
で、ログインに失敗したときのアクションを指定する。通常はsessions#new
に飛ぶようになっているが、ここではfailed
アクションを呼び出す様にしている。 -
その下に
failed
アクションを定義している。ログインフォームから渡されたURLをparams[:user][:url]
で取得し、リダイレクト先とする。 -
最後に、
routes.rb
にて、カスタマイズしたsessions_controller.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
def resource_name
:user
end
def resource
@resource ||= User.new
end
def devise_mapping
@devise_mapping ||= Devise.mappings[:user]
end
モデル名がUser
ではない場合は実際のモデル名に合わせる。
- なんだか無駄に遠回りしているような気がしますが、もっと簡単なやり方などあれば教えていただけると幸いです...。
##参考にさせたいただいたページ
- https://github.com/plataformatec/devise/wiki/How-To:-Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update
- https://github.com/plataformatec/devise/blob/master/app/controllers/devise/sessions_controller.rb
- https://code.i-harness.com/ja/q/58ffb7
- https://qiita.com/regashia/items/195d15be3cb40d0ddced
- http://hajimete-ruby.jugem.jp/?eid=84
- http://develop-hish-1ne.hatenablog.com/entry/2014/01/31/163704