suzy1031 / everyday-study-app

0 stars 0 forks source link

【history.vue】chart.jsのdata取得 #38

Closed suzy1031 closed 3 years ago

suzy1031 commented 3 years ago

chart.js使い方 参考 Vue.jsでChart.jsを使ってグラフを描写する Chart.jsで凡例を削除する chart.js:縦のラインを全部非表示にする chart.js 棒グラフ オプション色々 公式

# エラー
[Vue warn]: Invalid prop: type check failed for prop "chartData". Expected Object, got Null

参考 【vue-chart.js】Invalid propエラーを解決!

datasets.dataに渡す配列データを作成するメソッド

# メソッドを作成
+methods: {
+arrayData() {
# 配列を宣言
+var getData = [];
# 連想配列を作成(仮データ=>jsonで取得するデータを同じ型)
+var getData = [
+{key: value, key: value・・・}
+{key: value, key: value・・・}
+{key: value, key: value・・・}
・・・
+]
# mapを使って連想配列内からtimeを取り出して配列を作成する
+const times = getData.map(item => item.time)
+return times
+}
+},
# arrayData()の返り値を`data:`へ渡す
+fillData() {
+var data = this.arrayData()
・・・
+datasets: [
+{
・・・
+data: data
+}
+]
+}
map()メソッドを使うところとしては、連想配列から特定keyのvalueを取得したい時
for文ぐるぐる回す書き方より、1行で済む
可読性は上がる

参考 【Javascript】配列(オブジェクト)の操作【map/filter/some/reduce】

全ての集計をjs側でやるのは難しい...

suzy1031 commented 3 years ago

railsのactiveRecordで日別でsumしたデータへ整形してjsonでレスポンスした方が良さそう

# todo 仮データ=>今週のデータのみ取得するメソッドを追加
Study.select(:id, "sum(time) as time", :created_at).group("date(created_at)")
Study Load (0.6ms)  SELECT  `studies`.`id`, sum(time) as time, `studies`.`created_at` FROM `studies` GROUP BY date(created_at) LIMIT 11
=> #<ActiveRecord::Relation [
#<Study id: 1, time: 1.0, created_at: "2021-01-17 02:01:20">, 
#<Study id: 3, time: 4.0, created_at: "2021-01-18 03:00:00">, 
#<Study id: 5, time: 3.5, created_at: "2021-01-19 03:00:00">, 
#<Study id: 8, time: 0.5, created_at: "2021-01-21 03:00:00">, 
#<Study id: 9, time: 0.5, created_at: "2021-01-22 03:00:00">, 
#<Study id: 10, time: 1.0, created_at: "2021-01-23 03:00:00">
]>

routes.rbを設定

+get 'histories', controller: :studies, action: :history

studies_controller.rbを設定

-protect_from_forgery except: [:index, :create, :update, :destroy]
+protect_from_forgery except: [:index, :history, :create, :update, :destroy]

+def history
# todo modelメソッドにする. 今週のデータのみ取得する
+histories = current_user.studies.select(:id, "sum(time) as time", :user_id, +:created_at).group("date(created_at)")
+render json: histories
+end

jsonを確認してみる http://localhost:3000/api/v1/histories

[
{"id":1,"time":1.0,"user_id":1,"created_at":"2021-01-17T11:01:20.000+09:00"},
{"id":3,"time":4.0,"user_id":1,"created_at":"2021-01-18T12:00:00.000+09:00"},
{"id":5,"time":3.5,"user_id":1,"created_at":"2021-01-19T12:00:00.000+09:00"},
{"id":8,"time":0.5,"user_id":1,"created_at":"2021-01-21T12:00:00.000+09:00"},
{"id":9,"time":0.5,"user_id":1,"created_at":"2021-01-22T12:00:00.000+09:00"},
{"id":10,"time":1.0,"user_id":1,"created_at":"2021-01-23T12:00:00.000+09:00"}
]

必要なデータが取得できている

参考 ActiveRecordで日付ごとにgroup byしたい Railsガイド

suzy1031 commented 3 years ago

History.vueapiからjsonを受け取る

export default {
data() {
+histories: [],
}
}
mounted () {
+this.$http.secured.get('/api/v1/histories')
+.then(response => {
+this.histories = response.data
this.fillData()
+})
}
methods: {
arrayData () {
# todo データが無い曜日は0を配列に追加する
+var getData = this.histories;
const times = getData.map(item => item.time)
return times
}
}
fillData () {
this.loaded = false
+var data = this.arrayData()
this.chartData = {
labels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
datasets: [
{
barPercentage: 0.8,
backgroundColor: '#0066FF',
+data: data
}
]
}
this.loaded = true
},

jsonで取得したデータをchart.jsに渡してグラフを表示するまで完成 データが無い曜日は配列に0を代入する必要がある

# 現状の結果
[1/17(Sun): 1, 1/18(Mon): 4, 1/19(Tue): 3.5, 1/20(Wen): 0.5, 1/21(Thu): 0.5, 1/22(Fri):, 1]
labels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']に7つに対して、
配列が6つの為、左詰めにデータがマッピングされてしまっている
その為、土曜日のデータが無くなってしまっている

# 期待する結果
[1/17(Sun): 1, 1/18(Mon): 4, 1/19(The):3.5, 1/20(Wen): 0, 1/21(Thu): 1, 1/22(Fri): 0.5, 1/23(Sat): 1]
データが存在しない1/20(Wen)には0を渡し、日付と正しくマッピングする
suzy1031 commented 3 years ago

chartDataに渡す配列データ処理 考え方

arrayData () {
var getData = this.histories;
+if(getData.length < Const.INT_SEVEN) {
// (A)比較用の配列を作成する
+const arrayDayOfWeek = getData.map(item => item.day_of_week)
=> [0, 1, 2, 4, 5, 6]という配列を作成
// (B)比較対象の配列を用意する
+const arraySeven = [0, 1, 2, 3, 4, 5, 6]
// (A)と(B)を比較して差分を求める
+var resultArray = arraySeven.filter(i => arrayDayOfWeek.indexOf(i) == -1)
=> 返り値はarrayDyOfWeekに存在しない「3」
// 差分として返ってきた結果をindexに当てはめる
+for(var i = 0; i < resultArray.length; i++) {
+getData.splice(resultArray[i], 0, {time: 0})
=> 最終的にはtimeの値が欲しいので他のプロパティは不要
+};
+}
const times = getData.map(item => item.time)
return times
},