今回はRuby on Railsで簡単なチャート機能をajaxとchat.jsを使って実装します。例題としてプルダウンで選択した人物の成長記録を描写してみます。(横軸:記録日、縦軸:身長)
尚、Railsのバージョンは6.0.3.3を利用しています。
#ご留意ください
#テーブル定義
カラム名fi | カラム説明 | データ型 |
---|---|---|
id | ID | integer |
name | 名前 | string |
height | 身長 | float |
rec_date | 身長記録日 | date |
created_at | 登録日 | datetime |
updated_at | 更新日 | datetime |
今回はseeds.rb に二人分の身長記録データを登録しています。 |
||
#準備 | ||
##Gemに追記 |
gem 'chart-js-rails', '~> 0.1.4'
##yarnでパッケージを追加
yarn add jquery chart.js
##jQueryを扱えるようにする
const { environment } = require('@rails/webpacker')
// jqueryを使用できるようにする
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
module.exports = environment
##chart.jsモジュールの読み込み
// chart.js利用のため追加
require("chart.js")
##モデルの作成
rails generate model Height name:string height:float rec_date:date
rails db:migrate
##初期データの作成&投入
Height.create!(
[
{
name: 'たかし',
height: 150.3,
rec_date: "1990-01-03",
},
{
name: 'たかし',
height: 168.3,
rec_date: "1996-03-03",
},
{
name: 'たかし',
height: 178.4,
rec_date: "2003-04-03",
},
{
name: 'ゆき',
height: 130.3,
rec_date: "1987-05-07",
},
{
name: 'ゆき',
height: 144.1,
rec_date: "1995-04-23",
},
{
name: 'ゆき',
height: 153.6,
rec_date: "2000-05-13",
},
]
)
rails db:seed
##コントローラの作成
chart_sample_controller.rb
にindexアクションとsearchアクションを用意します。searchアクションは$.ajax()
を使ったリクエスト送信を受け取り、モデル内検索により名前に該当する身長のデータをjson形式で返すためのアクションになります。
rails generate controller ChartSample index search
ここまでで、必要なパッケージ、Gem、モデル、コントローラの作成まで終わりました。続いてルート設定とビューを用意します。
#ルート設定とビュー
##ルート設定
Rails.application.routes.draw do
get 'chart_sample/index'
get '/chart_sample/search', to: 'chart_sample#search'
end
##Views
javascript_pack_tag
でjsファイルを読み込んでいますが、ここではAjax+chart.jsを使ってグラフを描写するスクリプトをjQueryで記述しています。jsファイルはapp/javascript/packs/chart_sample/chart_user_height.js
に記述します。
<h1>成長記録</h1>
<div class="contents">
<select id="area">
<option value="0">選択してください</option>
<option value="1">たかし</option>
<option value="2">ゆき</option>
</select>
</div>
<div class="canvas">
<canvas id="myChart" width="400" height="400"></canvas>
</div>
<%= javascript_pack_tag 'chart_sample/chart_user_height' %>
ビューの画面は以下のようになります。
出来上がりはプルダウンで名前を選択→下に成長記録のグラフが表示されるといった具合です。
##Ajax+chart.js
ここのコードは長いので順を追って説明して最後にまとめてソースコードを貼ります。
$(document).on('turbolinks:load', function () {
$(function () {
// chart表示用の自作関数を読み込む
var chart_func = require("../origin_func.js")
$('#area').change(function() {
var selected_name = $('option:selected').text();
var selected_index = $('option:selected').val();
ここではAjaxの影響等によりページ遷移でjQueryが正常に作動しない現象を防ぐため、turbolinks:load
で初回読み込み時、リロードどちらでも発火するようにしています。origin_func.js
はchart.jsによるチャート描写機能を関数化したものを読み込んでいます。これは後ほど説明します。先ほどのビューのセレクトボックスのインデックスをselected_index
に、名前をselected_name
に定義しています。
if (selected_index > 0) {
// ajaxを使ってSelectBoxの値をjson形式でリクエスト送信する
// messages/searchesコントローラのindexアクションが受け取る
$.ajax({
type: 'GET', // リクエストのタイプ
url: '/chart_sample/search', // リクエストを送信するURL
data: { name: selected_name }, // サーバーに送信するデータ
dataType: 'json' // サーバーから返却される型
})
ここではselected_index>0
つまり名前(今回は「たかし」か「ゆき」)が選択された場合に処理される内容です。$.ajax()
を使って取得した名前をコントローラに送信します。先ほどのルート設定の通り、chart_sample_controller.rb
のsearchアクションへ送信されます。
chart_user_height.js
の続きの前にリクエストを受け取ったコントローラのsearch
アクションの内容について説明します。
class ChartSampleController < ApplicationController
def index
end
def search
# ↓検索処理のコード(検索結果の一覧が入力される)
@height = Height.where('name = (?)', "#{params[:name]}").order(rec_date: "ASC")
respond_to do |format|
# リクエストされるフォーマットがHTML形式の場合
format.html { redirect_to :root }
# リクエストされるフォーマットがJSON形式の場合
format.json { render json: @height }
end
end
end
search
アクションでは先ほどAjaxで送信されたリクエスト(人の名前)がparams[:name]に格納されているのでHeight
モデル内で一致検索し、モデルから返された中身のrec_date
を古い順に並び替えています。これは後ほど、chart.jsでグラフ表示する際に横軸をrec_date
にするためです。JSON形式でリクエストされているのでformat.json
が実行され、検索結果がJSON形式で返されます。分かりにくい場合はbinding.pry
などでデバッグしてみるとイメージがつきやすいと思います。
再びjsファイルに戻り描写処理についてみていきます。
// ajaxからリクエストを受けとり
.done(function (data) {
var height_val = [];
var height_name = [];
var height_date = [];
// chart.jsに渡すため配列に格納する
$(data).each(function(index, height) {
height_name.push(height.name);
height_val.push(height.height);
height_date.push(height.rec_date);
});
chart_func.bar_chart(document, 'myChart', "身長", height_date, height_val);
})
}
})
});
});
ここでは、モデルから受け取ったJSONフォーマットをchart.jsに渡すため配列型に直しています。例えばheight_val
はたかし君の過去の身長が配列として[(身長),(身長),(身長)]といった具合に入ります。(もう少しスッキリと書ける良い方法がある気がするのですが・・・)
そして、日付データと身長データが含まれた配列を読み込んだ自作関数に渡します。自作関数はchart.js
でChartをインスタンス化しています。関数に渡す引数はchart_func.bar_chart(document, canvasタグのid名, グラフに付けたいラベル名(任意), 横軸データの配列, 縦軸データの配列)
となります。
chart.jsによる描写処理
exports.bar_chart = function (document, id_name, label_name, data_x, data_y) {
var elm = document.getElementById(id_name).getContext('2d');
//既にチャートが描写されている場合はインスタンスを削除する
if(myChart.constructor === Chart){
myChart.destroy();
};
//棒グラフの描写
myChart = new Chart(elm, {
type: 'bar',
data: {
labels: data_x,
datasets: [{
label: label_name,
data: data_y,
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
})
};
if(myChart.constructor === Chart){
myChart.destroy();
};
ここのif
文による記述はcanvasを再利用するとグラフが前のグラフに重なって記述されてしまうため、もし既にChart
のインスタンスが作られている場合はインスタンスをChart.destroy
で削除する必要があるためです。myChart.constructor
は読み込み時点ではHTMLCanvasElement
からインスタンスが作成されるとChart
となります。
残りは配列データをもとにChartインスタンスを作成しています。
実装は以上になります。
chart_user_height.js
を途切れ途切れに説明したのでまとめて以下に貼り付けておきます。
$(document).on('turbolinks:load', function () {
$(function () {
// chart表示用の自作関数を読み込む
var chart_func = require("../origin_func.js")
$('#area').change(function() {
var selected_name = $('option:selected').text();
var selected_index = $('option:selected').val();
if (selected_index > 0) {
$("h1").css("color", "blue");
// ajaxを使ってSelectBoxの値をjson形式でリクエスト送信する
// messages/searchesコントローラのindexアクションが受け取る
$.ajax({
type: 'GET', // リクエストのタイプ
url: '/chart_sample/search', // リクエストを送信するURL
data: { name: selected_name }, // サーバーに送信するデータ
dataType: 'json' // サーバーから返却される型
})
// ajaxからリクエストを受けとり
.done(function (data) {
var height_val = [];
var height_name = [];
var height_date = [];
// chart.jsに渡すため配列に格納する
$(data).each(function(index, height) {
height_name.push(height.name);
height_val.push(height.height);
height_date.push(height.rec_date);
});
chart_func.bar_chart(document, 'myChart', "身長", height_date, height_val);
})
}
})
});
});
最後に
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。今後はグラフの表現を持たせたり、保存機能を付けたりしてきたいと思います。
ここまでお付き合いいただきありがとうございました!