Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Masatoshi Nishiguchi
Masatoshi Nishiguchi

Posted on • Edited on

     

Real-time charting with Elixir Phoenix.LiveView + Chart.js

This is written in Japanese. I might convert it to English later, maybe.

Goals

graph uneune Screen Recording 2021-11-28 at 5 33 48 PM

前提

  • 既存のPhoenixアプリがあり、それにグラフ用のLiveViewを追加する前提とします。
erlang             24.1.7elixir             1.13.0-otp-24phoenix            1.6.2phoenix_live_view  0.17.1
Enter fullscreen modeExit fullscreen mode

https://nagix.github.io/chartjs-plugin-streaming/2.0.0/guide/getting-started.html#integration

依存関係をインストール

npm install --save --prefix assets \  chart.js luxon chartjs-adapter-luxon chartjs-plugin-streaming
Enter fullscreen modeExit fullscreen mode
// package.json{"dependencies":{"chart.js":"^3.6.1","chartjs-adapter-luxon":"^1.1.0","chartjs-plugin-streaming":"^2.0.0","luxon":"^2.1.1",}}
Enter fullscreen modeExit fullscreen mode

グラフを操作するJavaScriptを定義

  • assets/js/line_chart.js
  • Chart.jsChartのラッパー
  • グラフの挙動を定義
  • グラフ初期化の関数
  • グラフに座標を追加する関数
// assets/js/line_chart.js// https://www.chartjs.org/docs/3.6.1/getting-started/integration.html#bundlers-webpack-rollup-etcimportChartfrom'chart.js/auto'import'chartjs-adapter-luxon'importChartStreamingfrom'chartjs-plugin-streaming'Chart.register(ChartStreaming)// A wrapper of Chart.js that configures the realtime line chart.exportdefaultclass{constructor(ctx){this.colors=['rgba(255, 99, 132, 1)','rgba(54, 162, 235, 1)','rgba(255, 206, 86, 1)','rgba(75, 192, 192, 1)','rgba(153, 102, 255, 1)','rgba(255, 159, 64, 1)']constconfig={type:'line',data:{datasets:[]},options:{datasets:{// https://www.chartjs.org/docs/3.6.0/charts/line.html#dataset-propertiesline:{// 線グラフに丸みを帯びさせる。tension:0.3}},plugins:{// https://nagix.github.io/chartjs-plugin-streaming/2.0.0/guide/options.htmlstreaming:{// 表示するX軸の幅をミリ秒で指定。duration:60*1000,// Chart.jsに点をプロットする猶予を与える。delay:1500}},scales:{x:{// chartjs-plugin-streamingプラグインの機能をつかうための型。type:'realtime'},y:{// あらかじめY軸の範囲をChart.jsに教えてあげると、グラフの更新がスムーズです。suggestedMin:50,suggestedMax:200}}}}this.chart=newChart(ctx,config)}addPoint(label,value){constdataset=this._findDataset(label)||this._createDataset(label)dataset.data.push({x:Date.now(),y:value})this.chart.update()}destroy(){this.chart.destroy()}_findDataset(label){returnthis.chart.data.datasets.find((dataset)=>dataset.label===label)}_createDataset(label){constnewDataset={label,data:[],borderColor:colors.pop()}this.chart.data.datasets.push(newDataset)returnnewDataset}}
Enter fullscreen modeExit fullscreen mode

LiveViewJavaScriptとの間で通信するためのフックを定義

LiveView がマウントされたときに実行する処理を書きます。

// assets/js/live_view_hooks/line_chart_hook.js// 前項で定義したJSファイルをインポートする。importRealtimeLineChartfrom'../line_chart'exportdefault{mounted(){// グラフを初期化する。this.chart=newRealtimeLineChart(this.el)// LiveViewから'new-point'イベントを受信時、座標を追加する。this.handleEvent('new-point',({label,value})=>{this.chart.addPoint(label,value)})},destroyed(){// 使用後はちゃんと破壊する。this.chart.destroy()}}
Enter fullscreen modeExit fullscreen mode

個人的にindex.jsファイルで整理するスタイルが気に入ってます。

// assets/js/live_view_hooks/index.jsimportLineChartfrom'./line_chart_hook'exportdefault{LineChart}
Enter fullscreen modeExit fullscreen mode

assets/js/app.jsファイルでLiveSocketにフックを登録します。

// assets/js/app.jsimport'phoenix_html'import{Socket}from'phoenix'import{LiveSocket}from'phoenix_live_view'importtopbarfrom'../vendor/topbar'importLiveViewHooksfrom'./live_view_hooks'letcsrfToken=document.querySelector("meta[name='csrf-token']").getAttribute('content')letliveSocket=newLiveSocket('/live',Socket,{hooks:LiveViewHooks,params:{_csrf_token:csrfToken}})// ...
Enter fullscreen modeExit fullscreen mode

グラフを表示するLiveViewを定義

# lib/mnishiguchi_web/live/chart_live.exdefmoduleMnishiguchiWeb.ChartLivedouseMnishiguchiWeb,:live_view@implPhoenix.LiveViewdefmount(_params,_session,socket)doifconnected?(socket)do# 本来はPubSubでデータを受信するところだが、今回そこはタイマーで再現する。:timer.send_interval(1000,self(),:update_chart)end{:ok,socket}end@implPhoenix.LiveViewdefrender(assigns)do~H"""    <div>      <!--      フックをセットする。      本LiveViewにおいてグラフ更新はJavascriptの責任範囲なので、あらかじめ`phx-update="ignore"`により      LiveViewにグラフ更新されないようにしておく。      -->      <canvas       n">chart-canvas"        phx-update="ignore"        phx-hook="LineChart"></canvas>    </div>    """end@implPhoenix.LiveViewdefhandle_info(:update_chart,socket)do# ダミーデータを生成し、"new-point"イベントを発信する。{:noreply,Enum.reduce(1..5,socket,fni,acc->push_event(acc,"new-point",%{label:"User#{i}",value:Enum.random(50..150)+i*10})end)}endend
Enter fullscreen modeExit fullscreen mode

LiveViewのルートを忘れずに定義する。

# lib/mnishiguchi_web/router.exdefmoduleMnishiguchiWeb.RouterdouseMnishiguchiWeb,:router# ...scope"/",MnishiguchiWebdopipe_through:browser# ...live"/chart",ChartLiveend# ...
Enter fullscreen modeExit fullscreen mode

graph uneune Screen Recording 2021-11-28 at 5 33 48 PM

比較的少ないコード記述量でリアルタイムグラフうねうねの実装ができました。

🎉🎉🎉

Resources

Top comments(1)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
mushu8 profile image
Alexandre Sagette
  • Joined

Thank you a lot for documenting this integration !!
It saves me some hours !

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

たのしむ
  • Location
    🇯🇵
  • Work
    ソフトウエアエンジニア
  • Joined

More fromMasatoshi Nishiguchi

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp