Chart.js in Elixir/Phoenix with Filtering

I was assigned to do the dashboard for this web application project were doing at my current job, and because it’s the dashboard, of course, you have charts. Considering that it’s just been months since I started doing backend stuff, I was quite pumped and curious on how to implement it on Elixir Phoenix (newbie alert 😂).

In this quick tutorial, I will show how to generate a chart with data coming from elixir/phoenix using poison. We will just focus on getting the Total # of Inquiries Per Month, to make it quick and easy.

What we need:

Chart.js: You can install it using npm or bower here or use this cdnjs

https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js

Poison: a JSON library for Elixir

Applying Chart.js

We will be using the Line Chart in this example, it’s quite easy to apply.

in dashboard.html.eex

<canvas id="lineChart" width="400" height="400"></canvas>

in dashboard.js

var ctx = document.getElementById("lineChart").getContext('2d');var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
datasets: [
{
label: "Total # of Inquiries",
backgroundColor: "rgba(155, 89, 182,0.2)",
borderColor: "rgba(142, 68, 173,1.0)",
pointBackgroundColor: "rgba(142, 68, 173,1.0)",
data: [inquiry_jan, inquiry_feb, inquiry_mar, inquiry_apr, inquiry_may, inquiry_jun, inquiry_jul, inquiry_aug, inquiry_sep, inquiry_oct, inquiry_nov, inquiry_dec]
}
]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});

The bold part is the real data separated per month coming from the database, we will discuss that later on.

Getting the data

Now that we have a chart, let’s start getting the data, and since we have a filter by year, let’s take care of that first.

in dashboard.html.eex

<%= form_for @conn, dashboard_path(@conn, :dashboard), [class: "ui action input", method: "get", as: "query"], fn f -> %>     <%= select f, :year, ["2017", "2018", "2019", "2020", "2021", "2022", "2023"], class: "ui search dropdown compact", onchange: "this.form.submit();" %><% end %>

It is a form get method as a query that has a select input passing year to our controller, this.form.submit() will load the page and pass a query every time we select a year.

in dashboard_controller.ex

def dashboard(conn, params) do query = params["query"] || %{"year" => "2018"}
inquiries_per_month = InquiryContext.get_inquiries_per_month(query)
conn
|> render(
"dashboard.html",
inquiries_per_month: inquiries_per_month
)
end

In the query we defined 2018 as default year incase params["query"] is empty, this will happen every first-page load.

in inquiry_context.ex

def get_inquiries_per_month(params) do 

year = Decimal.new(params["year"])
|> Decimal.to_float
query = Repo.all from m in Inquiry,
where: fragment("date_part('year', ?)", m.inserted_at) == ^year,
group_by: fragment("month"),
select: %{
month: fragment("to_char(?, 'Mon') as month", m.inserted_at),
count: count(m.id)
}
end

In this method, the fragment("date_part('year', ?)", m.inserted_at) will get the year in m.inserted_at and returns a float value, comparing it to the year the variable will give us all the inquiries by year. We also grouped the result by month, and in the select part we use: fragment("to_char(?, 'Mon') as month", m.inserted_at) to get the month part of m.inserted_at, this will return Jan, Feb, Mar etc. and then we count every record per month.

the result from the query

Mapping the data

We have the data, we just need to map them to our chart, and to do that we need poison.

in dashboard.html.eex

<script type="text/javascript">     window.inquiries_per_month = <%= raw(Poison.encode!   (@inquiries_per_month)) %></script>

This code will allow us to access the data in our javascript file dashboard.js.

in dashboard.js

var inquiry_jan = 0
var inquiry_feb = 0
var inquiry_mar = 0
var inquiry_apr = 0
var inquiry_may = 0
var inquiry_jun = 0
var inquiry_jul = 0
var inquiry_aug = 0
var inquiry_sep = 0
var inquiry_oct = 0
var inquiry_nov = 0
var inquiry_dec = 0
_.map(inquiries_per_month, (i) =>{ switch(i.month){
case "Jan":
inquiry_jan = inquiry_jan + i.count
break
case "Feb":
inquiry_feb = inquiry_feb + i.count
break
case "Mar":
inquiry_mar = inquiry_mar + i.count
break
case "Apr":
inquiry_apr = inquiry_apr + i.count
break
case "May":
inquiry_may = inquiry_may + i.count
break
case "Jun":
inquiry_jun = inquiry_jun + i.count
break
case "Jul":
inquiry_jul = inquiry_jul + i.count
break
case "Aug":
inquiry_aug = inquiry_aug + i.count
break
case "Sep":
inquiry_sep = inquiry_sep + i.count
break
case "Oct":
inquiry_oct = inquiry_oct + i.count
break
case "Nov":
inquiry_nov = inquiry_nov + i.count
break
case "Dec":
inquiry_dec = inquiry_dec + i.count
break
default:
console.log('no inquiries')

}
})

We loop the inquiries_per_month and assigned the count to a variable separated by month using a switch case.

For the last part, we just have to put the variables to the chart’s dataset, the bold one we saw earlier.

in dashboard.js

datasets: [
{
label: "Total # of Inquiries",
backgroundColor: "rgba(155, 89, 182,0.2)",
borderColor: "rgba(142, 68, 173,1.0)",
pointBackgroundColor: "rgba(142, 68, 173,1.0)",
data: [inquiry_jan, inquiry_feb, inquiry_mar, inquiry_apr, inquiry_may, inquiry_jun, inquiry_jul, inquiry_aug, inquiry_sep, inquiry_oct, inquiry_nov, inquiry_dec]
}
]

And we’re done !!!!

result:

Hope this article reaches someone like me. Happy Coding!!

Check out the full story on my website: Chart.js in Elixir/Phoenix with Filtering

I drink(☕️) and I code things — www.alvinrapada.com