[Ruby on Rails 5] Chartkick + Rails で連続していないデータをグラフ描画する
Active Record から引っ張ったデータを整形して扱います。
欲しい表示を得るまでに手間取ったので、導入と Tips のメモ。
- rails (5.1.4)
- chartkick (2.2.4)
- groupdate (3.2.0)
Contents
Installation
chartkick を bundle install
します。
gem 'chartkick'
グラフの描画に Chart.js を利用するように Chart.bundle
を合わせて指定します。
他のグラフライブラリも選択できます。
//= require Chart.bundle
//= require chartkick
Configuration
共通設定は config/initializers/chartkick.rb
に設定できます。
Chart.js の設定は、library:
以下に書けるよう。
細かな制御が必要な場合は、ライブラリのガイドを参考にする必要があります。
Chartkick.options = {
height: '400px',
library: {
layout: {
padding: {
left: 16,
right: 16,
top: 16,
bottom: 16
}
}
}
}
実際に調整すると、global 設定できる機能とできない機能があるようで悩みます。
また、バージョンによってオプション名が変わっているようです。
Usage
models/item
に対応するデータを models/ranking
にから引っ張り、ランキングチャートを描画します。
models/item
, models/ranking
は scaffold
等で既にあるものとします。
- 1 – 20 位にランクインしていると、date と rank に値が入ったレコードがある。
- ランク外の場合、レコードはない。
(models/ranking
の code
は特殊な値が入っているので、実際には別処理を挟んでいます。)
Model
models/ranking
にチャート用のクラスメソッドを用意します。
require 'date'
# == Schema Information
#
# Table name: rankings
#
# id :integer not null, primary key
# code :string
# rank :integer
# date :date
# created_at :datetime not null
# updated_at :datetime not null
#
class Ranking < ApplicationRecord
def self.chart(code)
return if code.blank?
data_array = select(:date, :rank).where(
code: code
).collect { |i| [i[:date].to_s, i[:rank]] }
return if data_array.blank?
x_values = data_array.map(&:first)
chart_data = complement_blank(data_array, x_values.min.to_s, x_values.max.to_s)
chart = { 'name': code, 'data': chart_data }
end
def self.complement_blank(data_array, date_from, date_to)
from_to = [date_from, date_to].map { |s| Date.parse(s) }
Range
.new(*from_to)
.each_with_object(data_array.to_h) { |date, h| h[date.to_s] ||= '21' }
.sort
end
end
chartkick には [["2015-11-08T19:59:57.000+08:00", 4], ["2015-11-09T00:02:37.000+08:00", 3]]
のような形式でデータを渡す必要があるようなので、 配列を整えます。
.collect { |i| [i[:date].to_s, i[:rank]] }
集計期間の 日付レンジ をデータから求めます。
x_values = data_array.map(&:first)
# x_values.min.to_s, x_values.max.to_s
このままでは日付が連続していないため X 軸がキレイに並びません。
下記でグラフ用に データのない日付を配列に追加 しました。
def self.complement_blank(data_array, date_from, date_to)
from_to = [date_from, date_to].map { |s| Date.parse(s) }
Range
.new(*from_to)
.each_with_object(data_array.to_h) { |date, h| h[date.to_s] ||= '21' }
.sort
end
Controller
前述のメソッドを controllers/items_controller
で呼び出し、インスタンス変数に入れます。
class ItemsController < ApplicationController
before_action :set_item, only: [:show, :edit, :update, :destroy]
# GET /items/1
def show
@ranking_chart = Ranking.chart(@item.code)
end
View
Chartkick で指定するオプションは、作り込むと長くなります。
views/items/show
で chart_ranking
ヘルパーを呼ぶ形にしておきます。
= ranking_chart
Helper
Chartkick のオプションと Chart.js のオプションを指定します。
- Chartkick
軸タイトル と 表示範囲 (1位から20位)をオプション指定します。
discrete: true
オプションで、省略せずに X軸のラベルをすべて表示 することができました。
module RankingsHelper
def ranking_chart
data = @ranking_chart
library_options = {
scales: {
xAxes: [{
gridLines: { drawOnChartArea: true }
}],
yAxes: [{
ticks: { reverse: true }
}]
}
}
line_chart data,
xtitle: 'Date', ytitle: 'Rank',
min: 1, max: 20,
discrete: true,
library: library_options
end
end
さらに Chart.js のオプションを library:
に指定していきます。
ランキング用途なので Y軸を反転 させます。
縦軸の表示オプションは drawOnChartArea
を利用する必要がありました。
概ねこのような流れで、必要なグラフを得ることができました。
JSON で非同期にグラフを描画する
グラフを非同期通信で描画するように変更します。
Routes
グラフのデータを ranking_chart_item_path GET /items/:id/ranking_chart(.:format)
のようなパスで渡すように設定を加えます。
Rails.application.routes.draw do
resources :items do
member { get :ranking_chart }
end
Helper
ヘルパーの参照先を変更します。
module RankingsHelper
def ranking_chart
library_options = {
scales: {
xAxes: [{
gridLines: { drawOnChartArea: true }
}],
yAxes: [{
ticks: { reverse: true }
}]
}
}
line_chart ranking_chart_item_path,
xtitle: 'Date', ytitle: 'Rank',
min: 1, max: 20,
discrete: true,
library: library_options
end
end
Controller
インスタンス変数から、JSON で値を渡すように変更します。
class ItemsController < ApplicationController
before_action :set_item, only: [:show, :edit, :update, :destroy, :ranking_chart]
# GET /items/1
def show
end
# GET /items/1/ranking_chart.json
def ranking_chart
result = Ranking.chart(@item.code)
render json: result
end
以上で完了です。
Tutorial
Chartkick 公式で紹介されているチュートリアルが分かりやすいのでオススメです。