Powered by AppSignal & Oban Pro

Bind Dropdown Events to Charts

02_bind_dropdown_events.livemd

Bind Dropdown Events to Charts

Mix.install([
  {:plotly_ex, "~> 0.3"},
  {:kino, "~> 0.18"}
])

Section

The idiomatic Elixir approach: use Kino.Control.form/2 with report_changes: true to drive chart updates from Elixir. Every dropdown change fires the listener with all current field values, so there's no need to call Kino.Input.read/1 from the callback process (which Kino forbids).

This gives full Elixir logic in the callback — filter data, fetch from DB, transform — unlike the native JS approach which can only restyle/relayout the existing data.

alias Plotly.{Figure, Scatter, Bar}
alias Plotly.Kino.PlotlyLive

# Dataset
categories = ~w[Mon Tue Wed Thu Fri Sat Sun]
data = %{
  "Sales"    => [120, 145, 110, 160, 200, 230, 180],
  "Traffic"  => [340, 390, 300, 420, 510, 600, 480],
  "Returns"  => [12, 8, 15, 10, 18, 22, 16]
}

make_fig = fn chart_type, metric ->
  key = metric |> to_string() |> String.capitalize()
  y = data[key]
  trace = case chart_type do
    :bar     -> Bar.new(x: categories, y: y, name: key)
    :line    -> Scatter.new(x: categories, y: y, mode: "lines+markers", name: key)
    :scatter -> Scatter.new(x: categories, y: y, mode: "markers", name: key,
                  marker: %{size: 12})
  end
  Figure.new()
  |> Figure.add_trace(trace)
  |> Figure.update_layout(title: "#{key} — #{chart_type}")
end

form = Kino.Control.form(
  [
    chart_type: Kino.Input.select("Chart type", [bar: "Bar", line: "Line", scatter: "Scatter"]),
    metric: Kino.Input.select("Metric", [sales: "Sales", traffic: "Traffic", returns: "Returns"])
  ],
  report_changes: true
)

chart = make_fig.(:bar, :sales) |> PlotlyLive.new()

Kino.listen(form, fn %{data: %{chart_type: ct, metric: m}} ->
  PlotlyLive.push(chart, make_fig.(ct, m))
end)

Kino.Layout.grid([chart, form])