Elixir Phoenix.LiveView + Chart.js를 사용한 실시간 차트 작성

이것은 일본어로 쓰여 있습니다. 나중에 영어로 변환할 수도 있습니다.

목표


  • Phoenix.LiveView + Chart.js + chartjs-plugin-streaming으로 실시간 차트 작성
  • 차트를 push에서 LiveView까지 Chart.js으로 구동

  • LiveView 상태 없음

  • LiveView은 타이머와 가짜 데이터
  • 를 사용하여 시뮬레이션하는 PubSub을 통해 여러 클라이언트로부터 새 데이터를 수신합니다.

  • LiveViewphx-hook를 통해 데이터를 차트로 보냅니다.
  • x 축: JavaScript 코드가 차트에 데이터 포인트를 추가하는 시점
  • y 축: 당사LiveView에서 보낸 값
  • 고유한 사용자 이름별로 데이터 세트 생성



  • 前提


  • 既存の Phoenix アプリがあり、それにグラフ用の LiveView を追加する前提とします.

  • erlang             24.1.7
    elixir             1.13.0-otp-24
    phoenix            1.6.2
    phoenix_live_view  0.17.1
    


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

    依存関係をinstorl




    npm install --save --prefix assets \
      chart.js luxon chartjs-adapter-luxon chartjs-plugin-streaming
    



    // package.json
    
    {
      "dependencies": {
        "chart.js": "^3.6.1",
        "chartjs-adapter-luxon": "^1.1.0",
        "chartjs-plugin-streaming": "^2.0.0",
        "luxon": "^2.1.1",
      }
    }
    


    그라후を操作する 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-etc
    import Chart from 'chart.js/auto'
    import 'chartjs-adapter-luxon'
    import ChartStreaming from 'chartjs-plugin-streaming'
    Chart.register(ChartStreaming)
    
    // A wrapper of Chart.js that configures the realtime line chart.
    export default class {
      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)'
        ]
    
        const config = {
          type: 'line',
          data: { datasets: [] },
          options: {
            datasets: {
              // https://www.chartjs.org/docs/3.6.0/charts/line.html#dataset-properties
              line: {
                // 線グラフに丸みを帯びさせる。
                tension: 0.3
              }
            },
            plugins: {
              // https://nagix.github.io/chartjs-plugin-streaming/2.0.0/guide/options.html
              streaming: {
                // 表示する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 = new Chart(ctx, config)
      }
    
      addPoint(label, value) {
        const dataset = this._findDataset(label) || this._createDataset(label)
        dataset.data.push({x: Date.now(), y: value})
        this.chart.update()
      }
    
      destroy() {
        this.chart.destroy()
      }
    
      _findDataset(label) {
        return this.chart.data.datasets.find((dataset) => dataset.label === label)
      }
    
      _createDataset(label) {
        const newDataset = {label, data: [], borderColor: colors.pop()}
        this.chart.data.datasets.push(newDataset)
        return newDataset
      }
    }
    


    LiveView 와 JavaScript 와 の間で通信するための福を定義



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

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


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

    // assets/js/live_view_hooks/index.js
    
    import LineChart from './line_chart_hook'
    
    export default {
      LineChart
    }
    

    assets/js/app.js 파이르로 LiveSocket に福クを登録します.

    // assets/js/app.js
    
    import 'phoenix_html'
    import { Socket } from 'phoenix'
    import { LiveSocket } from 'phoenix_live_view'
    import topbar from '../vendor/topbar'
    
    import LiveViewHooks from './live_view_hooks'
    
    let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute('content')
    let liveSocket = new LiveSocket('/live', Socket, {
      hooks: LiveViewHooks,
      params: {
        _csrf_token: csrfToken
      }
    })
    
    // ...
    


    그라후を表示する LiveView を定義




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


    LiveView의 루트를 취소할 수 있습니다.

    # lib/mnishiguchi_web/router.ex
    
    defmodule MnishiguchiWeb.Router do
      use MnishiguchiWeb, :router
    
      # ...
    
      scope "/", MnishiguchiWeb do
        pipe_through :browser
    
        # ...
        live "/chart", ChartLive
      end
    
      # ...
    




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

    🎉🎉🎉

    자원


  • https://online.pragmaticstudio.com/courses/liveview-pro/modules/31
  • https://qiita.com/torifukukaiou/items/e3056efc3d2c62600fa2
  • 좋은 웹페이지 즐겨찾기