Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

グラフうねうね (作り方 編) (Elixir/Phoenix)

この記事は、名前は聞いたことあるけど使ったことないやつをせっかくだから使ってみる Advent Calendar 2020 25日目(最終日) です。


はじめに

  • Elixir楽しんでいますか:bangbang::bangbang::bangbang:
  • PhoenixというWebアプリケーションフレームワークがありましてですね、今回はこれを楽しみたいとおもいます
  • macOS 10.15.7 を使っています

準備

  • ここがまあ、いろいろあるわけですが
  • 公式のInstallationに従ってください
    • Elixir 1.6 or later(2020/12/24現在の最新は1.11)
    • Erlang 20 or later(2020/12/24現在の最新は23)
    • phx_new
    • node.js
    • PostgreSQL
      • Phoenix assumes that our PostgreSQL database will have a postgres user account with the correct permissions and a password of "postgres".
    • inotify-tools (for linux users)
    • リンクも豊富に貼ってありますので、思い切って僕の胸に飛び込んで来てほしい
  • Elixirのインストールは、手前味噌ですがインストールなどをご参照ください

うまくいかなかったら

  • 何事にも準備が肝心です
  • ここが一番つまらないし、謎にハマってしまうことが多いのですが、がんばってください!
  • うまくいかなかったら、思い切って僕の胸に飛び込んで来てほしい (by 長嶋茂雄 読売ジャイアンツ終身名誉監督)
    • elixirjp.slack.com slack workspaceNervesJP workspaceに入ってきていただいて、@torifukukaiouへご質問ください
    • たとえ私が答えられなくても、マジみんな親切で優しい人が多いので、きっと解決できるでしょう:bangbang:

mix phx.new

$ mix phx.new nerves_jp_chart --live
$ cd nerves_jp_chart
$ mix setup
$ mix phx.server

welcome-to-phoenix.png

  • こんなのがブラウザに表示されましたでしょうか
  • もし表示されない場合はなにかしら不足している等でエラーがでているとおもいますので、エラーをひとつひとつ根気よく丁寧につぶしてください
  • ここを確実にしておかないと、楽しめないですよ!

ソースコードを書く

完成品

  • NervesJP/nerves_jp_chart
    • git diff 8b19624..の差分を全部取り込めば同じものができあがります :rocket::rocket::rocket:
  • これだと乱暴がすぎるので、
  • サンプルのグラフうねうねが動くところまでいっしょにやってみましょうかね
lib/nerves_jp_chart_web/live/chart_sample_live.ex
defmodule NervesJpChartWeb.ChartSampleLive do
  use NervesJpChartWeb, :live_view

  def mount(_params, _session, socket) do
    :timer.send_interval(1_000, self(), :next_values)
    socket = assign(socket, values: Jason.encode!([]), users: Jason.encode!([]), cnt: 0)
    {:ok, socket}
  end

  def handle_info(:next_values, socket) do
    new_cnt = socket.assigns.cnt + 1
    new_cnt = min(100, new_cnt)
    values = 1..new_cnt |> Enum.map(fn _ -> :random.uniform() end)
    users = 1..new_cnt |> Enum.map(&"user#{&1}")

    {:noreply,
     assign(socket,
       values: Jason.encode!(values),
       users: Jason.encode!(users),
       cnt: new_cnt
     )}
  end

  def render(assigns) do
    ~L"""
    <div id="data" data-values="<%= @values %>" data-users="<%= @users %>">
    <div phx-update="ignore">
      <canvas id="myChart" phx-hook="chart" width="200" height="200"></canvas>
    </div>

    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@latest/dist/chartjs-plugin-streaming.min.js"></script>
    <pre><%= @values %></pre>
    """
  end
end
lib/nerves_jp_chart_web/router.ex
  scope "/", NervesJpChartWeb do
    pipe_through :browser

    live "/", PageLive, :index
    live "/chart-sample", ChartSampleLive # add
  end
assets/js/app.js
/* add start */
let hooks = {}
hooks.chart = {
  mounted() {
    let colors = [
      'rgb(128, 128, 0)',
      'rgb(255, 255, 0)',
      'rgb(255, 0, 255)',
      'rgb(192, 192, 192)',
      'rgb(0, 255, 255)',
      'rgb(0, 255, 0)',
      'rgb(255, 0, 0)',
      'rgb(128, 128, 128)',
      'rgb(0, 0, 255)',
      'rgb(0, 128, 0)',
      'rgb(128, 0, 128)',
      'rgb(0, 0, 0)',
      'rgb(0, 0, 128)',
      'rgb(0, 128, 128)',
      'rgb(128, 0, 0)',
    ]

    var ctx = this.el.getContext('2d');
    new Chart(ctx, {
      // The type of chart we want to create
      type: 'line',
      // The data for our dataset
      data: {
        datasets: []
      },
      // Configuration options go here
      options: {
        scales: {
          xAxes: [{
            type: 'realtime',
            realtime: {
              delay: 2000,
              onRefresh: function(chart) {
                let len_datasets = chart.data.datasets.length;
                let dataEl = document.querySelector('#data');
                let len_users = JSON.parse(dataEl.dataset.users).length;
                for (let i = 0; i < (len_users - len_datasets); i++) {
                  chart.data.datasets.push({borderColor: colors[Math.floor(Math.random() * colors.length)], data: []})
                }

                chart.data.datasets.forEach(function(dataset, index) {
                  dataset.label = JSON.parse(dataEl.dataset.users)[index]
                  dataset.data.push({
                    x: Date.now(),
                    y: JSON.parse(dataEl.dataset.values)[index]
                  });
                });
              }
            }
          }]
        }
      }
    });
  }
}
/* add end */

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks}) /* hooks を追加 */

git diff 8b19624..にはもっと多くの差分があるよ

  • Yes, it is.
  • 2020/12/27開催の【オンライン】豪華プレゼント付!Elixir/Nerves(ナーブス)体験ハンズオン!では、みなさまのRaspberry Piからデータを打ち上げてもらうハンズオンを行いました(もう過去形:bangbang:)
  • その関係でいろいろありますですよ
    • データ保存関係のいろいろを追加しています
    • Dockerで動かすためのいろいろを追加しています
    • デプロイしやすいように変更を加えています
    • まあ、そんなところです
  • JS側のonRefreshで1秒ごとのタイミングをとって描画しているのですが、もっといいPhoenix LiveViewらしい書き方があるかもしれません
    • $\huge{ぜひおしえてください!!!}$
$ git diff 8b19624.. --stat
 .env.sample                                                     |   3 +
 .gitignore                                                      |   2 +
 Dockerfile                                                      |  53 +++++++++++++++++
 README.md                                                       |  14 +++++
 assets/js/app.js                                                |  62 +++++++++++++++++++-
 config/prod.exs                                                 |   7 ++-
 config/{prod.secret.exs => releases.exs}                        |   6 +-
 docker-compose.yml                                              |  33 +++++++++++
 elixir_buildpack.config                                         |   2 +
 entrypoint.sh                                                   |  14 +++++
 lib/mix/tasks/db_all_delete.ex                                  |  10 ++++
 lib/nerves_jp_chart/accounts.ex                                 |  25 ++++++++
 lib/nerves_jp_chart/accounts/user.ex                            |  18 ++++++
 lib/nerves_jp_chart/measurements.ex                             | 113 +++++++++++++++++++++++++++++++++++++
 lib/nerves_jp_chart/measurements/value.ex                       |  20 +++++++
 lib/nerves_jp_chart/release.ex                                  |  24 ++++++++
 lib/nerves_jp_chart_web/controllers/fallback_controller.ex      |  24 ++++++++
 lib/nerves_jp_chart_web/controllers/humidity_controller.ex      |  26 +++++++++
 lib/nerves_jp_chart_web/controllers/temperature_controller.ex   |  26 +++++++++
 lib/nerves_jp_chart_web/controllers/value_controller.ex         |  46 +++++++++++++++
 lib/nerves_jp_chart_web/live/chart_humidity_live.ex             |  63 +++++++++++++++++++++
 lib/nerves_jp_chart_web/live/chart_live.ex                      |  54 ++++++++++++++++++
 lib/nerves_jp_chart_web/live/chart_sample_live.ex               |  37 ++++++++++++
 lib/nerves_jp_chart_web/live/chart_temperature_live.ex          |  63 +++++++++++++++++++++
 lib/nerves_jp_chart_web/router.ex                               |   8 +++
 lib/nerves_jp_chart_web/templates/layout/root.html.leex         |   3 +
 lib/nerves_jp_chart_web/views/changeset_view.ex                 |  19 +++++++
 lib/nerves_jp_chart_web/views/humidity_view.ex                  |  12 ++++
 lib/nerves_jp_chart_web/views/temperature_view.ex               |  12 ++++
 lib/nerves_jp_chart_web/views/value_view.ex                     |  16 ++++++
 phoenix_static_buildpack.config                                 |   1 +
 priv/repo/migrations/20201106233307_create_users.exs            |  13 +++++
 priv/repo/migrations/20201106234127_create_values.exs           |  14 +++++
 priv/repo/migrations/20201111115618_add_kind_time_to_values.exs |  12 ++++
 test/nerves_jp_chart/measurements_test.exs                      |  70 +++++++++++++++++++++++
 test/nerves_jp_chart_web/controllers/value_controller_test.exs  |  42 ++++++++++++++
 36 files changed, 962 insertions(+), 5 deletions(-)

Wrapping Up :christmas_tree::santa::santa_tone1::santa_tone2::santa_tone3::santa_tone4::santa_tone5::christmas_tree:

ありがとう! Qiita Advent Calendar 2020
$\huge{毎日が12月だったらいいのに!}$

スクリーンショット 2020-12-23 22.32.36.png

スクリーンショット 2020-12-23 22.33.08.png

日にち タイトル カレンダー
2020/12/01 「クラウドネイティブの ASP.NET Core マイクロサービスを作成してデプロイする」 をやってみる :lgtm::lgtm::lgtm: 求ム!Cloud Nativeアプリケーション開発のTips!【PR】日本マイクロソフト
2020/12/01 [87, 101, 32, 97, 114, 101, 32, 116, 104, 101, 32, 65, 108, 99, 104, 101, 109, 105, 115, 116, 115, 44, 32, 109, 121, 32, 102, 114, 105, 101, 110, 100, 115, 33] Elixir その2
2020/12/02 LiveView uploadsを動かす 🎉🎉🎉(Elixir/Phoenix) Elixir その2
2020/12/03 【毎日自動更新】QiitaのElixir LGTMランキング! Elixir
2020/12/03 ElixirでAtCoderのABC123を解いてみる! fukuoka.ex Elixir/Phoenix
2020/12/03 Surfaceをつかってみる(Elixir/Phoenix) Elixir その2
2020/12/03 Nervesで湯婆婆を実装してみる #NervesJP
2020/12/03 Phoenixで実装した湯婆婆をAzureで動かす。Azure Virtual Machinesを使うとEC2やVPSでやったことがあることとなんらの変わり無しになりそうで、せっかくDockerと仲良くなりはじめたのでAzureコンテナーで動かしてみる。もちろんHTTPS緑にしたいのでアプリケーションゲートウェイも使ってみる。 湯婆婆
2020/12/04 とあるサイトでのみ%HTTPoison.Error{id: nil, reason: :closed}が発生 (Elixir) Elixir その2
2020/12/05 二次元リストの操作(Elixir) Elixir その2
2020/12/05 I was born to love Elixir :lgtm::lgtm::lgtm: プログラミング技術の変化で得られた知見・苦労話【PR】パソナテック
2020/12/06 次の関数の第二引数なんだよなー(Elixir) Elixir その2
2020/12/07 WindowsでIExで日本語を使う(iex --werl) (Elixir) Elixir その2
2020/12/07 Azure Container InstancesでNervesアプリを開発する Docker
2020/12/08 CircleCIでmix testする、Gigalixirへデプロイする(Elixir/Phoenix) Elixir その2
2020/12/09 Nervesで書き込める場所 (Elixir) #NervesJP
2020/12/09 HEX_HTTP_CONCURRENCY=1 HEX_HTTP_TIMEOUT=120 mix deps.get (Elixir) Elixir その2
2020/12/10 1 = a (プログラミングElixir 第2版) Elixir その2
2020/12/10 Raspberry Pi 4 + Grove Base HAT for Raspberry Pi + Grove Buzzer + Grove ButtonでつくるNervesアラーム Raspberry Pi
2020/12/11 NimbleCSVのご紹介(Elixir) Elixir その2
2020/12/11 Nervesならできるもん! |> 本当にできんのか! (Elixir) Raspberry Pi
2020/12/12 String.replace/3 (Elixir) Elixir その2
2020/12/12 「kentaro/mix_tasks_upload_hotswap」を試してみる! ご本人が参加していらっしゃるカレンダーにて #NervesJP
2020/12/13 GigalixirでPORTを4000以外の値にするのはだめよ (Elixir) Elixir その2
2020/12/13 Grove Base HAT for RasPiは真っ直ぐグイっとさす Seeed UG
2020/12/14 Grove - Buzzer をNervesで鳴らす Seeed UG
2020/12/15 グラフうねうね (動かし方 編) (Elixir/Phoenix) #NervesJP
2020/12/16 Macro.camelize/1 (Elixir) 何でもOKなカレンダー
2020/12/17 AtCoderをElixirでやってみる 競技プログラミング
2020/12/18 GrovePi+ Starter Kit for Raspberry Pi A+,B,B+&2,3,4 (CE certified) 〜Nervesならできるもん!〜 Seeed UG
2020/12/19 0埋め (Elixir) 何でもOKなカレンダー
2020/12/20 [Elixir]Qiitaの自分の記事をエクスポートする 何でもOKなカレンダー
2020/12/21 1260 (Elixir 1.11.2-otp-23) Elixir その2 Advent Calendar 2020
2020/12/21 ここがへんだよ GET /api/v2/items (Elixir) 何でもOKなカレンダー
2020/12/22 String.jaro_distance/2 (Elixir) Elixir その2 Advent Calendar 2020
2020/12/23 「動的計画法を使う問題をElixirで関数型っぽく解いてみる」のFibonacci3をガード節を使って書き直してみる Elixir その2 Advent Calendar 2020
2020/12/24 @tamanugiさんのex_at_coderを使ってみる (Elixir) Elixir その2
2020/12/25 @g_kenkunさんのg-kenkun/kyopuroを使ってみる (Elixir) Elixir その2
2020/12/25 グラフうねうね (作り方 編) (Elixir/Phoenix) 名前は聞いたことあるけど使ったことないやつをせっかくだから使ってみる

ありがとナイス:flag_cn:Qiita Advent Calendar 2020
$\huge{毎日が12月だったらいいのに!}$

れっつじょいなす(Let's join us) :bangbang::bangbang::bangbang:
:point_down::point_down_tone1::point_down_tone2::point_down_tone3::point_down_tone4::point_down_tone5:
NervesJP Slackへの参加URL
:point_up::point_up_tone1::point_up_tone2::point_up_tone3::point_up_tone4::point_up_tone5:

https___qiita-user-contents.imgix.net_https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F240349%2F5ef22bb9-f357-778c-1bff-b018cce54948.png_ixlib=rb-1.2.png

fukuokaex
エンジニア/企業向けにElixirプロダクト開発・SI案件開発を支援する福岡のコミュニティ
https://fukuokaex.fun/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away