Phoenix Liveview chartjs setup
Project creation
In terminal use mix to create a new live view project without ecto.
mix phx.new chart --live --no-ecto
and press ‘y’ when asked to install dependency.
We are going to work on these following files.
lib > chart_web > live > page_live.ex
lib > chart_web > live > page_live.html.leex
lib > chart_web > templates > layout > root.html.leex
assets > js > app.js
Cleanup
In page_live.ex delete all other functions except mount. Modify mount function such that it returns only
{:ok, socket}
In page_live.html.leex delete entire stuff.
In root.html.ex replace the entire <header> block with
<h1>Phoenix LiveView Chart.js</h1>
Now lets start the server with
mix phx.server
Initial try
Let’s start by following the documentation at https://www.chartjs.org/docs/latest/getting-started/
Let’s add the following in page_live.html.leex. Here we are just rendering a canvas and including the chart.js library script.
<canvas id="myChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
Next the document suggest us to add the following in a js file. We have app.js where we can add the following. We are not changing anything. We just want follow the documentation and observe the result.
var ctx = document.getElementById('myChart').getContext('2d');
var chart = new Chart(ctx, {
// The type of chart we want to create
type: 'line',
// The data for our dataset
data: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'My First dataset',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: [0, 10, 5, 2, 20, 30, 45]
}]
},
// Configuration options go here
options: {}
});
When we restart the server using mix phx.server
, Voilà!! we get a glimpse of chart.
:( But it shrinks.
Towards Hooks
Here we are going to work with both chart.js library and liveview. So let’s check the documentation at https://hexdocs.pm/phoenix_live_view/js-interop.html
Let’s add phx-hook=”chart”
to canvas.
If we run again we are greeted with an error as follows in the browser console.
unknown hook found for "chart" <canvas id="myChart" phx-hook="chart">
Gaining some insights from the error and the documentation, we add the following hook to the app.js. Note that we moved the chart creation from outside to mounted function of chart hook.
let hooks = {}
hooks.chart = {
mounted() {
var ctx = this.el.getContext('2d');
var chart = new Chart(ctx, {
// The type of chart we want to create
type: 'line',
// The data for our dataset
data: {
labels: ['January', 'February', 'March',
'April', 'May', 'June', 'July'],
datasets: [{
label: 'My First dataset',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: [0, 10, 5, 2, 20, 30, 45]
}]
},
// Configuration options go here
options: {}
});
}
}
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks})
We also changed ctx from
document.getElementById('myChart').getContext('2d');
to
this.el.getContext('2d');
as we can get the element through this.el.
We then pass the hooks along with params in LiveSocket creation.
Now we are able to see a chart, without any collapse.
Updating the chart using server push
Let’s check how to update the chart at https://www.chartjs.org/docs/latest/developers/updates.html
As per the documentation we have to change the data
and just issue a chart.update()
command.
The updates we push from server are received using this.handleEvent(
event_name, callback
)
inside mount.
The updates are sent using socket |> push_event(
event_name, payload
)
from the server.
this.handleEvent("points", ({points}) => {
chart.data.datasets[0].data = points
chart.update()
})
defmodule ChartWeb.PageLive do
use ChartWeb, :live_view
@impl true
def mount(_params, _session, socket) do
schedule_update()
{:ok, socket}
end
@impl true
def handle_info(:update, socket) do
schedule_update()
{:noreply, socket |> push_event("points", %{points: get_points})}
end
defp schedule_update, do: self() |> Process.send_after(:update, 2000)
defp get_points, do: 1..7 |> Enum.map(fn _ -> :rand.uniform(100) end)
end
In order to simulate server push, we just schedule and update message. On arrival of that we are pushing random values to the client.
Another interaction
If we add another interaction like incrementing a count on press of a button, it breaks.
page_live.ex and page_live.html.leex
@impl true
def mount(_params, _session, socket) do
schedule_update()
{:ok, socket |> assign(count: 0)}
end
@impl true
def handle_event("increment", _, socket) do
{:noreply, socket |> update(:count, &(&1 + 1))}
end
<h2><%= @count %></h2>
<button phx-click="increment">INCR</button>
This happens if we update the UI after the hook has been mounted.
In order to solve this we have to ignore the UI updates on canvas. So we enclose canvas with a container div which has phx-update=”ignore”
<div phx-update="ignore">
<canvas id="myChart" phx-hook="chart"></canvas>
</div>
Combine button and chart
Let’s combine button interaction and chart updation as follows.
page_live.ex and page_live.html.leex
@impl true
def mount(_params, _session, socket) do
{:ok, socket}
end
@impl true
def handle_event("next", _, socket) do
{:noreply, socket |> push_event("points", %{points: get_points()})}
end
<button phx-click="next">NEXT RANDOM CHART</button>
Now on each button press a chart with random values is generated.
Codebase @