Interactive charts with Ruby on Rails, StimulusReflex, and ApexCharts19 Sep 2022
Every B2B web app eventually gets charts. Users love them, buyers love them, sales teams love them. Look at all this data! Look how pretty it is! Charts!
I love charts too. So, let’s build a filterable pie chart with Rails and StimulusReflex. Our chart will rely on data from the database, and, because it is 2022, we will be able to update the chart as the user applies a filter without requiring a full page turn.
To render our charts, we will use ApexCharts, but the technique shown in this article will work fine with any other frontend charting library, I just like ApexCharts.
When we are finished, we will have a Rails app with a chart that looks like this:
(It looks nicer when it isn’t being captured as a low quality gif!)
Before beginning, this article assumes that you are comfortable with Ruby on Rails and that you have a passing familiarity with Stimulus and that you have written a Stimulus controller or two before. You won’t need any prior experience with StimulusReflex to follow along with this article.
As always, you can find the completed code accompanying this article on Github.
Let’s dive in!
To skip some uninteresting configuration steps, start today by cloning this repo to your local machine, working from the main branch. Make sure you have node installed and redis running locally.
Applicant, which will be used to provide data to the fancy chart we are going to build.
Applicants can’t be interacted with in the UI. A seed file has been provided to build enough test data for our chart to use.
From your terminal, set everything up with:
And then boot the app and build assets with:
Head to localhost:3000 and see the beautiful “dashboard”, where the new chart will live by the end of this article.
Adding a static chart
Install ApexCharts from your terminal:
To interact with ApexCharts we will use a Stimulus controller. From your terminal again:
Head to the generated Stimulus controller at
Later in this article, this
PieChartController will call a server-side reflex, so we extend this controller from the base StimulusReflex
initialize method, we render a new
ApexCharts chart, passing in the configuration options with
More details on the options available here can be found in the ApexCharts docs.
labels are set using Stimulus’ values. Stimulus values allow us to read and write HTML data attributes, providing a simple way to pass data from the server to our ApexCharts instance. Let’s connect this controller to the DOM to see how that looks in practice.
app/views/dashboards/index.html.erb and update it:
Here we use the
data-pie-chart-series-value to pass in the server generated data, and then render the chart in the empty
Before this will do anything but render an empty chart we need to set the values of
@series in the controller action.
app/controllers/dashboard_controller.rb like this:
Nothing too fancy here, just Ruby. We query the database for all of the applicants grouped by the applicant’s status, which in our case will be either “active” or “inactive”.
The query returns a hash, with the status values as a the keys and the number of applicants in each status as the values, something like this:
The keys become the labels in our pie chart and the values become the series data in the pie chart.
With this change in place, refresh the dashboard and you should see the pie chart render, like this:
Your percentage of applicants in each status will probably be different than mine, but you should have a chart!
Right now the pie chart just shows us the status of all applicants in the database which isn’t very useful. Let’s add a filter to drill down to applicants for a specific job, applying the new results without a page turn.
Add job name filter to chart
We want users to be able to filter our chart by the name of a job posting. To do this, we will add a select input to the dashboard. When the user changes the value of the select, we will query the database for data specific to the selected job and re-render the chart with updated data.
To start, let’s add the select input to the dashboard. In
Note the addition of the
<form> input and the
select_tag that rolls up all of the jobs in the database and plucks the name and id. In a real application, we might want to be a little more selective in building the option list, but our database only has three jobs so it will be okay.
Also on the
select is an associated Stimulus
action. When the select changes,
pie-chart#update will be called.
update doesn’t exist in our Stimulus controller yet, so let’s add that now.
Hey, StimulusReflex code!
this.stimulate, a method provided by StimulusReflex that we have access to in
PieChartController because it extends
As described in the docs, stimulate allows us to call server-side reflexes (in this case, the yet to be created
serializeForm option to our
After the reflex runs,
afterUpdate tells ApexCharts to re-render our chart with the handy
updateOptions method provided by ApexCharts.
Before changing the job name select will do anything but generate an error, the
PieChart reflex that our Stimulus controller calls must exist.
From your terminal, create the reflex:
Fill that new file in:
Accessing the form data from the client side is as easy as
params[:job_id] because of the
serializeForm option in our
stimulate call on the client side. Using the job id, we retrieve updated applicant details from the database and split that data out into
@series instance variables.
Here the really important thing to note is that we are setting the same instance variables that the
index method in the
DashboardController sets. Recall that in the
index method we only set the value of
@series if they are not already set:
When we call a reflex method, StimulusReflex reruns the current page’s controller action and renders that page’s ERB template after processing the reflex, while retaining any instance variables we have set, as described in this handy diagram from the StimulusReflex documentation.
Because we are only updating a piece of the page, we could use a selector morph instead of the default page morph. If we wanted to be really efficient, we could use a CableReady operation combined with a nothing morph to only update the two data attributes that we care about without touching anything else on the page.
For our use case, a page morph works fine. Selector and nothing morphs are more precise tools that come in handy as your pages get larger and use cases get more complex. We could also fiddle a bit with the
index method in the
DashboardController. When StimulusReflex reruns the
index method, we make an unnecessary database call, retrieving
data that we will never use. If we wanted to, we could ensure that the
index method skips the
data query when
@series are not set, but that’s an unnecessary optimization for our tutorial.
After the reflex finishes, the
afterUpdate life-cycle callback in the Stimulus controller runs which triggers ApexCharts to animate in the changes to chart.
One last step before this will work. In
retrieve_data, we use a
for_job scope that doesn’t exist yet.
Head over to
app/models/applicant.rb to add that scope:
Now we are all set. Refresh the dashboard, change the job name input and see the chart smoothly animate in the changes to the data.
Great work following along today, you’ve reached the end of this tutorial!
Today we worked through building a filterable chart that retrieves new data from the server without requiring a page turn. To do this, we used Ruby on Rails along with Stimulus and StimulusReflex to power the chart interactivity, and ApexCharts to save us from trying to build our own chart renderer in a brief tutorial.
This is a small, simple example of what you can do with StimulusReflex and CableReady, and hopefully it provides a good idea of the simplicity and flexibility of one of my favorite libraries in Rails-land.
To learn more about StimulusReflex, the docs are the best place to start. Note that the team is nearing final release of 3.5, a long-in-the-works release that will bring with it support for installing StimulusReflex with a single command.
Until 3.5 officially releases, installing StimulusReflex into a non-webpacker Rails app requires a bit of manual work. Marco Roth’s Stimulus Reflex esbuild repo outlines the steps to get up and running manually and will be a great resource until the 3.5 release. Thanks to Marco for providing his example repo, which served as my starting point for this tutorial.
If you enjoyed this article, you might enjoy my (free) book, Hotwiring Rails, from which is article was adapted. In the book, we build a fully functional Rails 7 application from scratch, focusing on adding interactive features with Turbo, Stimulus, CableReady, StimulusReflex, and friends.
As always, thanks for reading!