Phoenixでelm-vega  -  JavaScriptからportsでヘッドレスElm

　先日、[elm-vegaでグラフ描画 - JavaScriptからportsでヘッドレスElm - Qiita](https://qiita.com/sand/items/46c0f16552b3fc7470d9)でelm-vegaライブラリを紹介しました。今回はPhoenix上でこのライブラリを使ってみたいと思います。

　それでは早速プロジェクトを開始しましょう。

```
mix phx.new phoenix_elm_vega --no-ecto
cd phoenix_elm_vega
```

　PhoenixでElmを使えるように、elm-brunchをインストールします。

```
cd assets/
npm install --save-dev elm-brunch
```

　設定ファイルを修正して(A)と(B)を追加します。

```js:assets/brunch-config.js
#
  paths: {
    // Dependencies and current project directories to watch
//---------- (A)"elm"を追加
    watched: ["static", "css", "js", "elm",  "vendor"],
//----------
    // Where to compile files to
    public: "../priv/static"
  },

  // Configure your plugins
  plugins: {
    babel: {
      // Do not use ES6 compiler in vendor code
      ignore: [/vendor/]
    },
//---------- (B)elmBrunch項を追加
    elmBrunch: {
      elmFolder: "elm",
      mainModules: ["Walkthrough.elm"],
      outputFolder: "../vendor"
    }
//----------
  },
#
```


　assetsの下にelmディレクトリを作成し、elm-vegaライブラリをインストールします。

```
mkdir elm
cd elm
elm-package install gicentre/elm-vega
```

　今回のプログラムは公式サイト[elm-vega - Declarative visualization for Elm](https://github.com/gicentre/elm-vega)のexampleでHelloWorld.elmを利用します。

　helloWorld.htmlの内容で、app.html.eexを置き換えます。変更点はhelloWorld.jsを明示的にロードしている行を削除した点と、 Elm.HelloWorld.worker()行を削除して、app.jsをロードする行を追加したことです。 Elm.HelloWorld.worker()行はapp.jsに移しました。helloWorld.jsはbrunchが暗黙的にロードしてくれます。

```html:lib/phoenix_elm_vega_web/templates/layout/app.html.eex
<!DOCTYPE html>

<head>
  <title>Hello World from Elm-Vega</title>
  <meta charset="utf-8">

  <!-- These scripts link to the Vega-Lite runtime -->
  <script src="https://cdn.jsdelivr.net/npm/vega@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-lite@2"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-embed@3"></script>

</head>

<body>
  <!-- The id indicates the container in which to embed the Vega-Lite output. -->
  <div id="vis"></div>
 
  <script src="<%= static_path(@conn, "/js/app.js") %>"></script>
</body>

</html>
```

　次にapp.jsの末尾に次の行を挿入します。ここでElmのヘッドレスプログラムを動作させます。ElmコードのHelloWorld.elmのコンパイル結果helloWorld.jsは自動的にロードされていることに注意しましょう。ですから、ここではElm.HelloWorldはすでに参照可能な変数となっています。

```js:assets/js/app.js
#
    Elm.HelloWorld.worker().ports.elmToJS.subscribe(function(specs) {
      // Change actions to true to display links to source, editor and image.
      vegaEmbed("#vis", specs, {actions: false}).catch(console.warn);
    });
#
```

　最後にElmプログラムを作成しておきます。

```elm:assets/elm/HelloWorld.elm
port module HelloWorld exposing (elmToJS)

import Platform
import VegaLite exposing (..)


myVis : Spec
myVis =
    toVegaLite
        [ title "Hello, World!"
        , dataFromColumns [] <| dataColumn "x" (Numbers [ 10, 20, 30 ]) []
        , mark Circle []
        , encoding <| position X [ PName "x", PmType Quantitative ] []
        ]



{- The code below is boilerplate for creating a headless Elm module that opens
   an outgoing port to JavaScript and sends the Vega-Lite spec (myVis) to it.
-}


main : Program Never Spec msg
main =
    Platform.program
        { init = ( myVis, elmToJS myVis )
        , update = \_ model -> ( model, Cmd.none )
        , subscriptions = always Sub.none
        }


port elmToJS : Spec -> Cmd msg
```


　以上で設定とプログラム作成を終了します。phoenixを起動してみましょう。

```
mix phx.server
```

ブラウザを開くと、以下のような画像が表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/61915/22b9939a-ea9f-68c9-bc49-c2504d869a93.png)


　さて、問題はこれからです。今回は前回と違うexampleを使いました。前回のexampleのvega-lite specに含まれる　"data":{"url":"data/seattle-weather.csv"},　の処理がphoenix上でうまく動作しなかったからです。phoenixの設定や使い方を変えれば、うまくいくのでしょうが、少なくとも最もシンプルなstaticの設定ではエラーとなりました。なぜかは今のところ不明ですが、elm-reactorでも同じ問題が起きているから、Vega-Lite runtimeの問題かもしれません。また原因がはっきりしたら報告するということで、今回はこれで終わりたいと思います。