This plugin allows you to render Chart.js charts in Phoenix LiveView applications easily and reactively.
Add chart_js to your list of dependencies in mix.exs:
def deps do
[
{:chart_js, "~> 0.1.0"}
]
endAfter adding the dependency, install the required npm packages:
# Install Elixir dependencies
mix deps.get
# Install Chart.js npm dependencies
mix chart_js.installTo set up the Chart.js plugin project itself:
mix setup- Add the component to your LiveView:
<.live_component
module={ChartJs.ChartComponent}
id="my_chart"
type="bar" # or "line", "pie", etc.
data={%{"labels" => ["A", "B"], "datasets" => [%{"label" => "Demo", "data" => [1,2]}]}}
options={%{}}
/>- Include the JS hook in your app.js
import ChartJsHook from "../deps/chart_js/assets/chart_hook.js";
let Hooks = { ChartJs: ChartJsHook };
let liveSocket = new LiveSocket("/live", Socket, { hooks: Hooks });- Done!
Whenever the data or options change, the chart will automatically update.
You can update charts dynamically using push_event from your LiveView:
# Add a new data point to ALL charts (push_event/3)
push_event(socket, "add_chart_data", %{
"label" => "March", # new label (optional)
"datasets" => [
%{"data" => 25} # new data point for first dataset
]
})
# Add data to multiple datasets
push_event(socket, "add_chart_data", %{
"label" => "April",
"datasets" => [
%{"datasetIndex" => 0, "data" => 30}, # first dataset
%{"datasetIndex" => 1, "data" => 15} # second dataset
]
})Note: With push_event/3, the event is sent to ALL chart components. If you need to target specific charts, you have two options:
# Send target in the event payload
push_event(socket, "add_chart_data", %{
"target" => "sales_chart", # chart component id
"label" => "Sales Data",
"datasets" => [%{"data" => 100}]
})# Different events for different charts
push_event(socket, "add_sales_chart_data", %{
"label" => "Q1", "datasets" => [%{"data" => 1500}]
})
push_event(socket, "add_users_chart_data", %{
"label" => "Q1", "datasets" => [%{"data" => 250}]
})defmodule MyAppWeb.ChartLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
if connected?(socket) do
:timer.send_interval(2000, self(), :update_chart)
end
socket = assign(socket, :chart_data, initial_chart_data())
{:ok, socket}
end
def handle_info(:update_chart, socket) do
new_value = :rand.uniform(100)
push_event(socket, "add_chart_data", %{
"label" => "Point #{System.system_time(:second)}",
"datasets" => [%{"data" => new_value}]
})
{:noreply, socket}
end
defp initial_chart_data do
%{
"labels" => ["Jan", "Feb"],
"datasets" => [
%{
"label" => "Sales",
"data" => [10, 20],
"backgroundColor" => "#3b82f6"
}
]
}
end
endYou can pass any valid Chart.js configuration in data and options.
%{"labels" => ["Red", "Blue"],
"datasets" => [
%{"label" => "Votes", "data" => [12, 19], "backgroundColor" => ["#f00", "#00f"]}
]}If you see this error when using the plugin as a local dependency:
✘ [ERROR] Could not resolve "chart.js/auto"
Solution:
-
Make sure you've installed Chart.js dependencies:
mix chart_js.install
-
Or install manually:
cd assets npm install chart.js -
Verify the import path in your
app.js:import ChartJsHook from "../deps/chart_js/assets/chart_hook.js";
Make sure each chart has a unique id:
<.live_component module={ChartJs.ChartComponent} id="chart_1" ... />
<.live_component module={ChartJs.ChartComponent} id="chart_2" ... />
Use the target parameter for selective updates:
push_event(socket, "add_chart_data", %{
"datasets" => [%{"data" => 42}]
}, target: "chart_1") # Only updates chart_1- The component renders a
<canvas>and uses a hook to initialize Chart.js. - The hook destroys the previous chart before creating a new one to avoid memory leaks.
- You can have multiple charts in the same view, just use different
ids.
The package can be installed by adding chart_js to your list of dependencies in mix.exs:
def deps do
[
{:chart_js, "~> 0.1.0"}
]
end