コード例
ここで作るのは下記のようなUIです。
デモはこちらに用意しています。
なおReactの場合はインテグレーションであるreact-chartjs-2を使うことが多いかと思います。しかし2024年12月4日現在、このインテグレーションのドキュメントサイトはダウンしていて閲覧できません。
ライブラリー選定の一般論としては、自分で簡単に実装できるものはライブラリーを使用せずに自分で実装するのが適切だと思います。下記に示す通り、Stimulusは簡単ですのでインテグレーションに頼らなくて済みます。
<% set_breadcrumbs [["ChartJS", component_path(:chartjs)]] %> <%= render 'template', title: "ChartJS", description: "" do %> <div data-controller="chartjs" data-chartjs-label-value="UFO sightings per year!!!!" data-chartjs-data-value="<%= [{ year: 2010, count: 10 }, { year: 2011, count: 20 }, { year: 2012, count: 15 }, { year: 2013, count: 25 }, { year: 2014, count: 22 }, { year: 2015, count: 30 }, { year: 2016, count: 28 }, ].to_json %>"> <div class="flex justify-between"> <% [2010, 2011, 2012, 2013, 2014, 2015, 2016].each_with_index do |year, index| %> <div> <div><%= year %>:</div> <input type="range" min="0" max="100" value="10" step="10" class="w-20" data-chartjs-bar-param="<%= index %>" data-action="input->chartjs#changeBar"/> </div> <% end %> </div> <div class="w-full"> <canvas data-chartjs-target="chart"></canvas> </div> </div> <% end %>
to_json
でJSONに変換しておけば、StimulusのValueとして正しく処理されます<input type="range" ...>
タグのところはデータを変更するスライダーです。ここがイベントを受け取るActionになります
data-action="input->chartjs#changeBar"
により、スライダーの値が変更されるとChartjsController
のchangeBar
メソッドが呼び出されますdata-chartjs-bar-param
で指定します<canvas data-chartjs-target="chart"></canvas>
はChartjsController
からの出力を受けるtarget
ですChartjsController
Stimulus controllerimport {Controller} from "@hotwired/stimulus" import Chart from "chart.js/auto" // Connects to data-controller="chartjs" export default class extends Controller { static values = { data: {type: Array, default: []}, label: String } static targets = ["chart"] connect() { } disconnect() { this.chart?.destroy() } changeBar(event) { const value = event.currentTarget.value const barIndex = Number(event.params.bar) const newDataValue = [...this.dataValue] newDataValue[barIndex] = {year: 2010 + barIndex, count: value} // We create a new dataValue object and use the // `this.dataValue` setter to update the value. // This is to trigger the `this.dataValueChange()` callback. // It is unnecessary if you call `this.#render()` directly // in this function. this.dataValue = newDataValue } dataValueChanged() { this.#render() } #render() { this.#renderChart() } #renderChart() { const data = this.dataValue if (this.chart) { this.chart.data = this.#data() this.chart.update() } else { this.chart = new Chart( this.chartTarget, { type: 'bar', data: this.#data() } ); } } #data() { return { labels: this.dataValue.map(row => row.year), datasets: [ { label: this.labelValue, data: this.dataValue.map(row => row.count) } ] } } }
static values
で使用するStimulus Valuesを宣言しています
data
はChartに表示するデータです。Array型として持ちます。HTMLのdata-chartjs-data-values
属性にJSON型でステートが保持されますlabel
はChartの表題になりますstatic targets
でControllerで処理されたデータの出力先を指定します
<canvas>
タグを指定します disconnect()
はライフサイクルに関するものです。このStimulus Controllerが画面から消えるなどした場合に呼び出されます。ここではChartjsオブジェクトを削除して、メモリリークを防ぎますchangeBar
はイベントハンドラです。スライダーをクリックして値を変更した時に呼び出されます
newDataValue
という新しいArrayを作ってthis.dataValue
にセットしている点です。古いthis.dataValue
の値を変更するだけではダメで、全く新しいArrayを渡す必要がありますdataValueChanged
コールバックが呼ばれません。この辺りはReactのステート変更と全く同じですdataValueChanged
はStimulus Valueステートが変更された時に自動的に呼ばれるコールバックです。この中で#render()
を呼びます#render()
ではさらに#renderChart()
を呼び出し、Chart.jsにデータを渡す処理を書いています。ここではChart.jsのチュートリアル通りのデータを渡しています
new Chart()
で作りますthis.chart
にセットされた既存のChartを書き換えてupdate()
を呼びますfetch()
でデータを取る必要はありません。また<script type="application/json">
などを使ってデータを埋め込む必要もありません