ここまででユーザの情報の表示と編集画面をつくることができました。
次に、bootstrap のサンプル風にメニューバーをつけていきます。メニューバーは、ログイン時にはアカウント情報とアカウント編集とログアウトのメニューを、ログインしていない場合はログイン画面へのリンクを表示することとします。
Controller ごとの layout の変更
Controller ごとに layout を指定するには、各コントローラで layout で app/views/layouts 以下のファイルを指定します。
例えば、account controller の表示レイアウトを変更したい場合、app/views/layouts/account.html.erb を用意し、accout controller で以下のように指定します。
1 2 3 4 5 6 7 8 |
class AccountsController < ApplicationController layout "account" before_action :authenticate_user! def show end end |
layout の設計
以下のように画面を想定します。
- ログインしていない状況でサーバにアクセスすると、home 画面を表示。メニューバーにはログインとサインアップへのリンク
- ログインへのリンクをクリックすると、ログイン画面を表示。ログイン画面にはメニューバー無し。
- サインアップへのリンクをクリックすると、サインアップ画面を表示。サインアップ画面にはメニューバー無し
- ログイン後は home 画面に戻る。メニューバーにはユーザ名とアバターを表示。クリックするとアカウント情報の表示とログアウトへのリンクを表示
- アカウント情報の表示と編集はメニューバーあり。ログイン状態のメニューバーを表示
ということで、ログイン画面を表示する app/controllers/users/sessions_controller.rb と app/controllers/users/registrations_controller.rb で devise 用のメニューバー無しのレイアウトを適用、それ以外は application 用のレイアウトで、ログイン状態かどうかでメニューバーの内容を帰ることにします。
devise 用の layout の用意と適用
これは単純に現状の application.html.erb がメニューバー無しの状態なので、これをコピーして、devise 関連の各コントローラで適用します。
1 2 |
$ cd app/views/layouts/ $ cp application.html.erb devise.html.erb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
$ cd app/controllers/users $ vi registrations_controller.rb class Users::RegistrationsController < Devise::RegistrationsController layout "devise" before_action :configure_sign_up_params, only: [:create] before_action :configure_account_update_params, only: [:update] def create super resource.build_profile resource.profile.name = resource.username resource.save end protected def configure_sign_up_params devise_parameter_sanitizer.permit(:sign_up, keys: [:username, :email]) end def configure_account_update_params devise_parameter_sanitizer.permit(:account_update, keys: [:username, :email]) end end $ vi sessions_controller.rb class Users::SessionsController < Devise::SessionsController layout "devise" before_action :configure_sign_in_params, only: [:create] protected def configure_sign_in_params devise_parameter_sanitizer.permit(:sign_in, keys: [:username]) end end |
このあと rails サーバを再起動してサインインしようとすると、以下のログが表示されて app/views/layouts/devise.html.erb を使用して表示されているのがわかります。
1 2 3 4 5 |
Started GET "/users/sign_in" for ::1 at 2019-09-23 18:24:56 +0900 Processing by Users::SessionsController#new as HTML Rendering users/sessions/new.html.erb within layouts/devise Rendered users/sessions/new.html.erb within layouts/devise (4.7ms) Completed 200 OK in 59ms (Views: 47.9ms | ActiveRecord: 0.0ms) |
application 全体用の layout の用意
app/views/layouts/application.html.erb にメニューを追記します。ログイン状態とそうでない状態の partial な view を定義して、ログインしているかどうかで切り替えます。
用意したファイルはこんな感じです。
1 2 3 4 5 6 7 8 9 10 |
app/views/ |-- layouts | |-- application.html.erb | |-- devise.html.erb | |-- mailer.html.erb | `-- mailer.text.erb |-- partial | |-- _menu_default.html.erb | |-- _menu_logged_in.html.erb | `-- _navbar.html.erb |
layouts 以下の application.html.erb で _navbar.html.erb を render partial で読み込み、_navbar.html.erb 内部でユーザがログインしているかどうかで _menu_logged_in.html.erb を使うか _menu_default.html.erb を使うかを判断して render partial で読み込みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!DOCTYPE html> <html> <head> <title>Devise Sample</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body class="application"> <%= render :partial => "partial/navbar" %> <main role="main"> <div class="container"> <div class="row"> <%= yield %> </div> </main> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 |
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark"> <a class="navbar-brand" href="/">Devise Test</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#devise_sample_navbars" aria-controls="devise_sample_navbars" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <% if user_signed_in? %> <%= render :partial => "partial/menu_logged_in" %> <% else %> <%= render :partial => "partial/menu_default" %> <% end %> </nav> |
1 2 3 4 5 6 |
<div class="collapse navbar-collapse" id="bs-navi"> <ul class="navbar-nav ml-auto"> <li class="nav-item"><%= link_to 'ログイン', new_user_session_path, :class => "nav-link text-white" %></li> <li class="nav-item"><%= link_to '登録', new_user_registration_path, :class => "nav-link text-white" %></li> </ul> </div><!-- navbar-collapse --> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div class="collapse navbar-collapse" id="bs-navi"> <ul class="navbar-nav ml-auto"> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <%= image_tag current_user.profile.avatar.to_s, :height => 26, :width => 26, :class => "rounded-circle" %> <span class="ml-1"><%= current_user.profile.name %></span> </a> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown"> <%= link_to "設定", account_path, :class => "dropdown-item" %> <%= link_to 'ログアウト', destroy_user_session_path, method: :delete, :class => "dropdown-item" %> </div> </li> </ul> </div><!-- navbar-collapse --> |
メニューに対応した style の指定
sticky なメニューを定義すると、コンテンツがメニューにかぶって表示されてしまうので、stylesheet で body を menu の分だけ下にずらします。また、各 view で上部に隙間を開けるために入れていた mt-5 を削除します。
今回は、application.scss は、もろもろの import のみを記述するポリシーとして、アプリケーション全体への css を記述する devise_test.scss を作成し、これを application.scss で import することにしました。
1 2 3 4 |
/* メニューの高さが 56px なので、14px ぐらい開けるようにした */ body { padding-top: 70px; }; |
1 2 3 |
@import "bootstrap"; @import "devise_test"; @import "account"; |
スクリーンショット
ここまでの変更でだいたい以下のような画面がみえるようになりました。
最初の画面
ログイン画面
登録画面
ログイン後
ドロップダウンメニュー
アカウント情報表示