PFのテーマカラーを選択できるようにする
PFのテーマカラーがデフォルトのピンクとブルー系の2つからユーザーが選択して設定できるようにしたいです。
進めていく手順
- Userテーブルにcolorカラムを追加する
- ユーザー新規登録ページとプロフィール編集ページにフォームを追加
- コントローラのストロングパラメータにcolor追加
- cssファイルを編集
まず最初にUsersテーブルにcolorカラムを追加しました。
$ bundle exec rails g migration AddColorToUsers color:integer Running via Spring preloader in process 70954 invoke active_record create db/migrate/20210714133754_add_color_to_users.rb
作成されたマイグレーションファイルにデフォルト値を追加します。
class AddColorToUsers < ActiveRecord::Migration[6.1] def change add_column :users, :color, :integer, default: 0 end end
マイグレートしてデータベースに追加しました。
$ bundle exec rails db:migrate == 20210714133754 AddColorToUsers: migrating ================================== -- add_column(:users, :color, :integer, {:default=>0}) -> 0.7259s == 20210714133754 AddColorToUsers: migrated (0.7260s) =========================
そしてuserモデルにcolorカラムについての記述を行います。
class User < ApplicationRecord # サイトのテーマカラー enum color: { pink: 0, blue: 1} end
pinkをデフォルトにします。
次にフォームをビューに追加していこうと思います。simple_formを使用しています。最初にユーザー新規作成画面
selectedでフォームの初期値を入れています。
users/new.html.erb <div class="form-group"> <%= f.input :color, as: :select, selected: 'pink', collection: User.colors.keys, class: "form-control", hint: "マイページのテーマカラーを選択してください" %> </div>
collection:
でセレクトフォームで選べる項目を入れています。Userのcolor項目のハッシュからキーを配列で取得しています。実際にコンソールで確認しながら作成しました。
Sayo-MacBook-Pro:emoji_diary SAYO$ rails c Running via Spring preloader in process 74168 Loading development environment (Rails 6.1.3.2) irb: warn: can't alias context from irb_context. irb(main):001:0> User.colors => {"pink"=>0, "blue"=>1} irb(main):002:0> User.create(nickname: '👓', name: "megane", password: 'password', password_confirmation: 'password', email: 'megane@example.com', color: 'blue') TRANSACTION (0.2ms) BEGIN User Exists? (0.4ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = 'megane@example.com' LIMIT 1 User Exists? (39.1ms) SELECT 1 AS one FROM `users` WHERE `users`.`name` = 'megane' LIMIT 1 User Create (88.9ms) INSERT INTO `users` (`nickname`, `name`, `crypted_password`, `salt`, `created_at`, `updated_at`, `email`, `color`) VALUES ('👓', 'megane', '$2a$10$AhqCyQAETZxQEjUZtl7qb.alfkxg.KS3zSgj5rW3s1A8ulbTS.y.e', 'o4tKY5Vtr8RWcxNxt8Fw', '2021-07-14 15:34:14.588242', '2021-07-14 15:34:14.588242', 'megane@example.com', 1) TRANSACTION (7.1ms) COMMIT => #<User id: 25, nickname: "👓", name: "megane", crypted_password: "$2a$10$AhqCyQAETZxQEjUZtl7qb.alfkxg.KS3zSgj5rW3s1A...", salt: "o4tKY5Vtr8RWcxNxt8Fw", created_at: "2021-07-15 00:34:14.588242000 +0900", updated_at: "2021-07-15 00:34:14.588242000 +0900", email: "megane@example.com", role: "general", color: "blue"> irb(main):005:0> user = User.last User Load (92.5ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1 => #<User id: 25, nickname: "👓", name: "megane", crypted_password: "$2a$10$AhqCyQAETZxQEjUZtl7qb.alfkxg.KS3zSgj5rW3s1A...", salt: "o4tKY5Vtr8RWcxNxt8Fw", created_at: "2021-07-... irb(main):006:0> user.color => "blue" irb(main):007:0> user = User.first User Load (29.2ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1 => #<User id: 1, nickname: "🐒", name: "sayo", crypted_password: "$2a$10$idRbazbxLnZamo9L0JOzuOf7Dy40FD7lK/GCUMb6O7/...", salt: nil, created_at: "2021-06-30 15:30:20.862197000 ... irb(main):008:0> user.color => "pink"
次にプロフィール編集ページのフォームに追加します。
<div class="form-group"> <%= f.input :color, as: :select, include_blank: false, collection: User.colors.keys, class: "form-control", hint: "マイページのテーマカラーを選択してください" %> </div>
include_blank: false
で空白で投稿できないようにします。
そしてフォームを追加したのでストロングパラメーターにcolorを追加して、データを受け取れるようにします。
class UsersController < ApplicationController def user_params params.require(:user).permit(:nickname, :name, :password, :password_confirmation, :email, :color) end end
class ProfilesController < ApplicationController def user_params params.require(:user).permit(:nickname, :name, :password, :password_confirmation, :email, :color) end end
cssファイルにblueのテーマカラーを追加していきます。私が考えた実装の仕方なのでとても冗長的で汚いコードになっていると思うし、こんなこと絶対にしない!って思われるかもしれないです。でも一応思ったように動いたので、これで良しとしました。
やったこと
まず最初にapplication.scss
ファイルにデフォルトのpinkの色をcss変数を使って定義していきます。
:root { --color1: #fbe6de; --color2: #c76e54; --color3: #f5cdbd; --color4: #bf9288; --color5: #f5ece4; }
色をつける部分で下記のように指定します。全て変更してサイトの色が変わらず出力されているか確認しました。
#header { background-color: var(--color1); }
次に青色のテーマを管理するapplication_blue.scss
ファイルを作成しました。これをapplication.scssファイルの代わりに使いたいので、必要なcssファイルや、bootstrapの記述も書き写しました。
@import 'home'; @import 'simple_calendar'; @import 'diaries.scss'; @import 'users.scss'; @import 'profiles.scss'; @import 'header_footer.scss'; @import '~bootstrap/scss/bootstrap'; @import '~@fortawesome/fontawesome-free/scss/fontawesome'; .blue { --color1: #ABBDDA; --color2: #399ECC; --color3: #7097C3; --color4: #444f7c; --color5: #E0ECFF; }
そしてapplication.html.erb
ファイルで条件分岐を使って、colorがpinkとログインしていない時のcssファイルでapplication.scss
ファイルを使うこと、そしてそれ以外ではapplicaction_blue.scss
ファイルを使用することを定義していきます。
<!DOCTYPE html> <% if !logged_in? || (current_user.color == "pink") %> <html> <head> <title><%= page_title(yield(:title)) %></title> <meta name="viewport" content="width=device-width,initial-scale=1"> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_pack_tag 'application', media: 'all' %> <%= javascript_pack_tag 'application' %> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script> </head> <body> <% if logged_in? %> <%= render 'shared/header' %> <% else %> <%= render 'shared/before_login_header' %> <% end %> <%= render 'shared/flash_message' %> <%= yield %> <%= render 'shared/footer' %> </body> </html> <% else %> <html class="blue"> <head> <title><%= page_title(yield(:title)) %></title> <meta name="viewport" content="width=device-width,initial-scale=1"> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_pack_tag 'application_blue', media: 'all' %> <%= javascript_pack_tag 'application' %> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script> </head> <body> <% if logged_in? %> <%= render 'shared/header' %> <% else %> <%= render 'shared/before_login_header' %> <% end %> <%= render 'shared/flash_message' %> <%= yield %> <%= render 'shared/footer' %> </body> </html> <% end %>
タグから切り替えるようにしているのでとても長いコードになってしまいました。最初はstylesheet_pack_tag
のコードを下記のように条件分岐した感じで書いていたのですがcssファイルの:root
と書いている部分の記述でうまくいかなかったのでこのようになりました。
blueに変更するときは<html class="blue">
と書いてクラスを当てています。
ローカル環境ではこの記述で問題なくテーマカラーを変更することができました。しかしherokuにpushして本番環境で動作するか確認すると500エラーが出てしましました。エラー文を残していなくてとても後悔しています。。唯一検索履歴に残っていたWebpacker can't find application_blue.css
これがエラーの部分です。webpackerがapplication_blue.cssファイルを見つけられない?といっています。調べていくと同じようなエラーが出ている人を見つけました。
https://github.com/rails/webpacker/issues/2071
config/webpacker.yml
の中でextract_css: true
の場合にはlinkを出力し、extract_css: false
の場合にはJavaScriptを使って動的にCSSを読み込むという動作になります。development環境ではfalseになっていたのでproduction環境でもfalseに設定しました。
production: <<: *default # Production depends on precompilation of packs prior to booting for performance. compile: false # Extract and emit a css file extract_css: false # Cache manifest.json for performance cache_manifest: true
詳しい説明が下記に載っていました。
https://zenn.dev/ryouzi/articles/da8a77accc221e#stylesheet_pack_tagがproduction環境でエラーが出る