25
ChartJS with Rails 6 and Webpacker
Setting up ChartJS on a vanilla Rails 6 application
For a long time now I wanted to build an uptime tracker with a clean developer friendly UX. Having used Pingdom in my workplace I found it quite inflexible and I "think" I can do better. π€
My professional experience was in RoR although pre Rails 6 and still using Sprockets. When building my uptime tracker I wanted to use ChartJS for visualizations and it took me a surprisingly long time to figure out how to do so. So I thought I'd share my findings.
Sprockets is a ruby library for compiling and serving web assets.
The latest version of Rails (I'm not sure at what point they made this switch) uses Webpacker.
Think of webpacker as an alternative to sprockets. It makes use of WebPack under the hood to help manage JS in Rails.
When using JS with Webpacker it's important to understand scope. I still haven't wrapped my head around using webpacker properly yet, but know this, Webpack does not make modules available to the global scope by default. This is part 1 of what tripped me up.
Make sure you have rails installed. I generally prefer to use
rbenv
. It makes it easier to manage different ruby versions.rails new chartjs-example
- In your shell run
rails generate controller Home index
- Then edit
routes.rb
.
Rails.application.routes.draw do
root to: 'home#index'
end
rails s
then on your web browser go to localhost:3000
. It should show you an empty page.
You now have a Vanilla Rails 6 application with a default home page.
yarn add chart.js
/app/javascript/packs/application.js
add then following import Chart from 'chart.js/auto';
You need to include
import Chart from 'chart.js/auto';
as simply including require 'chart.js'
will not work. You can choose to import specific modules by following the instructions here.This is part 2 of what tripped me up for a long time.
app/views/home/index.html.erb
and add the following
<canvas id="myChart" width="400px" height="400px"></canvas>
<script>
var myChart = new Chart(ctx, {...});
</script>
No you can't. Like I mentioned before, webpacker doesn't include things in the global scope by default. If you did the above and visited the page, you'd find your console throwing the error message
Uncaught ReferenceError: Chart is not defined
Instead you need to do the following
application.js
. As you've imported your ChartJS modules there, Chart
can be accessed within that scope.
document.addEventListener('turbolinks:load', () => {
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: JSON.parse(ctx.canvas.dataset.labels),
datasets: [{
data: JSON.parse(ctx.canvas.dataset.data),
}]
},
});
})
Notice that you've not defined your datasets yet. i.e.
labels
(x axis) and data
(y axis). You need to do that using the data-
attributes in html.index.html.erb
and replace the previous canvas line with
<canvas id="myChart" width="200px" height="100px" data-labels="<%= @data_keys %>" data-data="<%= @data_values %>" ></canvas>
app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
@data_keys = [
'January',
'February',
'March',
'April',
'May',
'June',
]
@data_values = [0, 10, 5, 2, 20, 30, 45]
end
end
Where
data_keys
and data_values
are populated with sample data.If you now go to
http://localhost:3000/
you'll see your newly created chart.
data-
attributesSince I sometimes really want to see the whole file in a tutorial instead of just the change you need to make.
config/routes.rb
Rails.application.routes.draw do
root to: 'home#index'
end
app/views/home/index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
<canvas id="myChart" width="200px" height="100px" data-labels="<%= @data_keys %>" data-data="<%= @data_values %>" ></canvas>
app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
@data_keys = [
'January',
'February',
'March',
'April',
'May',
'June',
]
@data_values = [0, 10, 5, 2, 20, 30, 45]
end
end
app/javascript/packs/application.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
import Chart from 'chart.js/auto';
Rails.start()
Turbolinks.start()
ActiveStorage.start()
document.addEventListener('turbolinks:load', () => {
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: JSON.parse(ctx.canvas.dataset.labels),
datasets: [{
data: JSON.parse(ctx.canvas.dataset.data),
}]
},
});
})
If you found this useful please let me know! :D
25