今回やったこと
すでにx軸が日付、Y軸がカロリーの棒グラフは作ってあり、そこにY軸に体重の折れ線グラフを追加していきます。
グラフ実装前の下準備
体重入力フォームを追加
.weight
体重
= f.number_field :weight, step: '0.1', placeholder: '例)70'
kg
体重を保存するためのカラムを追加
https://qiita.com/azusanakano/items/a2847e4e582b9a627e3a#%E3%82%AB%E3%83%A9%E3%83%A0%E8%BF%BD%E5%8A%A0
この記事参考にすればすぐできます。
ストロングパラメーターにも体重カラムを追加
posts_controller.rb
private
def post_params
params.require(:post).permit(:food, :calorie, :protein, :fat, :carbo, :text, :image, :weight).merge(user_id: current_user.id)
end
体重を表示
_post.html.haml
.weight
- if post.weight.present?
= post.weight
kg
実際のページ(表示されるとこうなる)
いよいよグラフに体重を追加していく!!
さて、ここからグラフにどうやってこの追加した体重を載せていくか?
現状ではヨコに体重、タテにカロリーだけ。
ここにタテに体重の折れ線グラフも追加して、カロリーと体重の相関を見れるようにしたい。
↓
わりとすぐに参考記事が見つかって意外と1時間でできました。
完成コードはこちら
app/controllers/charts_controller.rb
class ChartsController < ApplicationController
def index
# カロリー
# 日付ごとで分けてカロリー合計を算出
sum_calorie = current_user.posts.group("date(created_at)").sum(:calorie)
# 日付ごとのカロリー合計がハッシュの形なので値を取得して配列に入れて変数に代入
array_calorie = sum_calorie.values
# gonを使ってデータをjs側に渡す
gon.data = []
# mapメソッドで日付ごとのカロリー合計を1つずつ取り出す
# mapメソッドの使い方 → 配列変数.map {|変数名| 具体的な処理 }
gon.data = array_calorie.map{ |calorie| calorie}
# 日付ごとにまとめてそのうちcreated_atカラムだけ取得。配列の形で格納されている
dates_calorie = current_user.posts.group("date(created_at)").select(:created_at)
gon.date = []
@dates = dates_calorie.map{ |dates| dates.created_at}
# each文で日付の表記を1つずつ取り出して変える
@dates.each do |a|
gon.date << a.strftime("%Y年%m月%d日")
end
# 体重 ☆今回この部分を追加しました☆
gon.weight = current_user.posts.group("date(created_at)").select(:weight).map{ |weight| weight[:weight]}
end
end
app/view/charts/index.html.erb
<%# canvasタグ内にグラフを描く %>
<div class="chart-container" style="position: relative; height:50vh; width:50vw">
<canvas id="myChart"width="400" height="400"></canvas>
</div>
<%# htmlのscriptタグ内にjsを書いていく %>
<script>
# グラフ部分のDOMを取得しgetContextメソッドでグラフを描く
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
# labelsでX軸を指定
labels: gon.date,
datasets: [{
type: 'bar', # カロリーは棒グラフ
label: "1日のカロリー合計",
# dataでY軸を指定
data: gon.data,
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1,
fill: false
},
#今回はこの部分を追加しました----------------------------
{
type: 'line', #体重は折れ線グラフ
label: "体重",
# dataでY軸を指定
data: gon.weight,
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgb(255, 99, 132)',
borderWidth: 1.2,
#これを書かないと棒線の下の部分が塗られてしまう
fill: false,
}]
#------------------------------------------------------
},
options: {
title: {
display: true,
text: "カロリーグラフ"
},
scales: {
yAxes: [{
ticks: {
beginAtZero:true
},
id: 'y左軸'
}]
}
}
});
</script>
<div class="home">
<%= link_to 'ホームに戻る', root_path %>
</div>
出来上がったグラフがこちら
参考にした記事と完成までのプロセス
http://www.dcom-web.co.jp/lab/javascript/draw_multi_axis_graph_using_chartjs
最初に参考にしたのがこのページ。
- いきなりgonで渡さず、最初は上記記事を参考に[60, 70]と適当に数字を置いて表示されるか試した。
- それがうまくいって、無事グラフが表示されることを確認したら、どうしたら体重をコントローラーからビューに受け渡せるか考えた。
- 1日で1つの体重をどうやって取得するか難しそうだなと思っていたが、Railsのgroupメソッドを使うと、そのグループごとにidが最も小さいものにまとめてくれることがわかった。日付ごとにgroupメソッドでまとめれば、その日投稿されたpostsの中で最もidが小さいweightを取得できる。https://pikawaka.com/rails/group#group%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E4%BD%BF%E3%81%84%E6%96%B9
- あとはgonに代入してgonからgonへ受け渡す。
- 最後に
fill: false,
を追加して線の形にして終了。