Using ECharts in Elixir Livebook
Tags: elixir, livebook, echarts
Published: 2023-08-08 04:37:01

I have been using a lot of Elixir [Livebook](https://livebook.dev/) lately and I am having a lot of fun with data exploration and analysis using Livebook. Livebook has great support for drawing charts using the excellent Vega-Lite library. Even though Vega-Lite is great and is sufficient for many usecases I wanted to try using [Apache ECharts](https://echarts.apache.org/en/index.html) inside Livebook. We use ECharts at work and are happy with it.

I looked around and could not find any prior work/library that allowed me to use ECharts in Livebook so I decided to create a hex package for Livebook that used ECharts to draw charts. Its called [lb_echarts](https://github.com/parth-patil/lb_echarts/) check it out if you are interested.

After some research I found that [Kino.JS](https://hexdocs.pm/kino/Kino.JS.html) could be used to integrate custom JS into Livebook.

To use `Kino.JS` I had to `use Kino.JS` in my module and then define the `init` JS function within the `asset` macro. This looks as follows. Note in the following code I have only shown how do define the `asset` section, later I will show how to write rest of the code.

```elixir
defmodule LbEcharts do
  @moduledoc """
  Library to use Echarts in LiveBook
  """

  use Kino.JS
 
  asset "main.js" do
    """
    export async function init(ctx, chart_data) {
      await ctx.importJS("https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js");

      ctx.root.innerHTML = `
        <div id="echart_window" style="width: 800px;height:500px;"></div>`

        var myChart = echarts.init(document.getElementById('echart_window'));

        // Specify the configuration items and data for the chart
        var options = JSON.parse(chart_data);

        // Display the chart using the configuration items and data just specified.
        myChart.setOption(options);
    }
    """
  end
end
```

The entire contents of the `asset` section are enclosed in triple quotes. Whatever you write inside these triple quotes are considered as JS code. In the above code you will note that I have used `async` and `await` in the JS code. This is because I am loading a JS file and I want to wait for the JS file to be loaded before executing rest of the code.

The `init` function in JS gets the context in `ctx` variable and second argument is what was passed to the `Kino.JS.new()` function. One can define the HTML markup for the output Livebook cell in the `ctx.root.innerHTML` attribute. Note how the markup is enclosed in the backticks.

Now lets look at how we implement a bar chart.

```elixir
defmodule LbEcharts do
  @moduledoc """
  Library to use Echarts in LiveBook
  """

  use Kino.JS

  def new(chart_data) do
    Kino.JS.new(__MODULE__, chart_data)
  end

  def bar(opts) do
    options =
      %{
        "title" => %{"text" => opts.title},
        "tooltip" => %{},
        "legend" => %{"data" => opts.legend},
        "xAxis" => %{"data" => opts.x},
        "yAxis" => %{},
        "series" => [
          %{
            "name" => "sales",
            "type" => "bar",
            "data" => opts.y
          }
        ]
      }
      |> Jason.encode!()

    LbEcharts.new(options)
  end

  asset "main.js" do
    """
    export async function init(ctx, chart_data) {
      await ctx.importJS("https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js");

      ctx.root.innerHTML = `
        <div id="echart_window" style="width: 800px;height:500px;"></div>`

        var myChart = echarts.init(document.getElementById('echart_window'));

        // Specify the configuration items and data for the chart
        var options = JSON.parse(chart_data);

        // Display the chart using the configuration items and data just specified.
        myChart.setOption(options);
    }
    """
  end
end
```

Following is how you would use the library to draw a bar chart

```elixir
LbEcharts.bar(%{
  title: "Bar Chart",
  legend: ["sales"],
  x: ["shirt", "cardign", "chiffon shirt", "pants", "heels", 
  "socks"],
  y: [5, 20, 36, 10, 10, 20]
})
```

To learn more check out [lb_echarts](https://github.com/parth-patil/lb_echarts/)