0. はじめに
少しずつ開発している日報アプリのlytnote。日報を毎日記入していくけど、なにか良いフィードバックが欲しいところ。ラジオ体操のスタンプカードのような、githubの草のような。
というわけでカレンダーをつくって、日報があったところが塗られるようにした。
1. datepickerの導入
カレンダーを自前で作るのはかなり大変なので、有名なライブラリを使うことにした。ここはjQuery UIのdatepickerだろう。
導入は簡単で、CDNを貼るだけだった。以下のサイトからここからできる。
https://releases.jquery.com/
私の場合はapplication.html.erb
に貼った。
<!-- 略 -->
<head>
<title>lytnote</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<!-- 略 -->
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<!-- 略 -->
</head>
あとはviewファイルとjsファイルは以下のようにする。
<!-- 略 -->
<div id="datepicker"></div>
<!-- 略 -->
$(document).on("page:load turbolinks:load", function() {
//略
$( "#datepicker" ).datepicker();
});
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("jquery")
require("@nathanvda/cocoon")
require ('home')
これでdatepickerが表示される。
2.「特定の日」を取得してデータを受け渡す
日報一覧の画面が出た時点で、特定の日、つまりは日報が作成された日付の配列を取得する。コントローラを以下のようにする。
def index
@reports= Report.includes(:report_items).where(user_id: current_user.id).page(params[:page]).order(reported_on: :desc)
reported_days_original = @reports.map(&:reported_on)
reported_days = reported_days_original.map{
|days| days.strftime("%F")
}
@reported_days = reported_days.to_json
end
#略...
@reports
云々は気にしなくていい。それ以降が重要だ。
railsでは指定したカラムのデータを取得するのに、pluck
というメソッドがある。しかし、@reports.pluck(:reported_on)
とは使用できない。(使用できるけど値が重複してしまったりする。)
以下のような理由かららしい。
知っておくと良い注意点は、リレーションオブジェクトにincludesがあると、eager loadingが不必要なクエリにおいてでも、pluckがeager loadingを引き起こすことです。
https://railsguides.jp/active_record_querying.html#pluck
このeager loadingが詳しくわからなかった。ひとまずはmapで置き換えることにした。
-
days.strftime("%F")
によってreported_daysの配列の要素を2023-01-01
のように変換している。
- to_jsonを使って、railsからJSへデータの受け渡しを行えるように整形しておく。
viewファイルに以下を追記する。
<!-- 略 -->
<input type="hidden" id="reported" value="<%= @reported_days %>">
<div id="datepicker"></div>
<!-- 略 -->
inputタグをhiddenにして記入しておくと、表示せずにデータだけ受け渡すことが可能となる。
3. カレンダーの特定の日付にCSS classを付与
特定の日付にだけclassを付与させる場合はbeforeShowDay
メソッドを使う。functionと戻り値の記述方法は以下のようにする。
$( "#datepicker" ).datepicker({
beforeShowDay: function(date) {
return [false, '', ''];
}
});
functionのなかのdateには日付が入っている。Chromeのデベロッパーツールで確認すると、一ヶ月とその前後3~4日を一つずつループして確認しているようだ。戻り値は配列で記述しなければいけない。それぞれ
[日付選択の可否(true/false),CSSのclass名,日付にカーソルをあてると出てくる値]
となる。うしろの二つは""と空白にしても構わない。
もう少し詳しく書くと以下になる。
$(document).on("page:load turbolinks:load", function() {
//略...
let reportedDays_json = document.getElementById('reported').value;
if (reportedDays_json !== null) {
let reportedDays = JSON.parse(reportedDays_json);
$( "#datepicker" ).datepicker({
beforeShowDay: function(date) {
let formattedDay = dayjs(date).format('YYYY-MM-DD');
if (reportedDays.indexOf(formattedDay) != -1) {
return [false, 'reported-days', ''];
}else{
return [false, '', ''];
}
}
});
}
});
reportedDays
は日報が登録された日付の配列となる。beforeShowDay
メソッドで、ひとつひとつのdateがそのなかに入っているかどうか確認する。入っていたらreported-days
classを付与させる。
dayjs(date).format
は dayjsを利用している。導入方法は以下が詳しい。
4. CSSを変更
今のままだとデフォルトのdatepickerのデザインになってしまう。デザインを確かめながらCSSを変更していく。
/* 略..*/
.ui-datepicker-next:hover,.ui-datepicker-prev:hover {
background: none;
}
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default,
.ui-button,
html .ui-button.ui-state-disabled:hover,
html .ui-button.ui-state-disabled:active {
border: 0px;
background-image: none;
background:inherit;
font-weight: normal;
color: #182736;
}
.ui-state-disabled,
.ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled {
opacity: 1;
filter:none; /* support: IE8 */
}
これで背景のグラデーションが消えて、すこし今風のデザインになる。こだわると時間がかかりそうなので、一旦はここまで。
4. 完成画面
5. おわりに
少しずつ自分の思うサービスになってきた気がする。これからも開発を続けるぞ。
今回の機能はまだ本番環境には反映させてません。もう少しいろいろなおしてからあげます。