StimulusJS on Rails 101

Younes Serraj
Younes SerrajOct 23, 2018
StimulusJS is the new Javascript framework that’s been published this year

StimulusJS is the new Javascript framework that’s been published this year

StimulusJS is the new Javascript framework that’s been published this year (2018) by DHH, the creator of Ruby on Rails.

This is a practical guide to start using StimulusJS in a Ruby on Rails project (with a bonus at the end 😉). You’ll also find at the end of the article a link to a project that uses all that’s presented in this article for you to try it out of the box.

You might be tempted to compare it to other frameworks such as ReactJS, Angular or VueJS but you should not. StimulusJS doesn’t play in their field since it doesn’t intend to build user interface components, synchronize frontend and backend using AJAX or any of this stuff.

As of today, StimulusJS’ unique job is to bind behaviors to HTML elements.

A real competitor would be JQuery’s .on() method. JQuery is great but it forces you to define your bindings in Javascript files, which means you can’t know if an HTML element has a bound behavior unless you read all Javascript files. I prefer the StimulusJS approach to it: read an HTML element attributes to know if a behavior is bound or not. Now don’t get me wrong, while I delegate the binding part to StimulusJS, I still use JQuery to make my life easier when it comes to frontend development (AJAX calls, etc.). The two work well together.

A quick word about JQuery and Turbolinks

If you ever used JQuery and Turbolinks together, you’ll be happy to know that there is no more of this:

$(document).ready()

And no more of this:

$(document).on('turbolinks:load', function() {})

StimulusJS takes care of this now :) Put your code in your controllers and let the framework do the magic. What controllers? How? Where? We’ll talk about this in a bit. Let’s first install the dependencies to run StimulusJS in a Ruby on Rails project, then we’ll see how to use it.

A quick word about controllers

This might be obvious to experienced frontend developers but I want to make sure no one gets confused:

Rails has its controllers under app/controllers and StimulusJS has its controllers under app/javascript/controllers. There is no link between them, none.

In this article, when I refer to a controller, I’m talking about a StimulusJS one.

Install StimulusJS in Rails using Yarn and Webpacker

1. Install Yarn

Rails shifted to Yarn for Javascript packages management. Yarn has a great community, handles dependencies, provides caching, etc. Now when you need to add an asset dependency to your project (Javascript but also stylesheets), I strongly suggest you first look for the yarn package before you look for the gem.

TL;DR: Bootstrap, MomentJS, ChartJS, Fontawesome, etc.: add them through Yarn.

To add a package, use the searchbox in https://yarnpkg.com/ to find it, then install it using yarn add [package name].

2. Install Webpacker

Nowadays, Rails applications are not always just plain Ruby on Rails code. You often have a Rails API, a React/Vue/Angular/YouNameIt Javascript frontend and some SCSS stylesheets to make it all look pretty. In this context, we consider the Javascript frontend part as an app on its own for it often comes with its own controllers, router, models, etc.

There are two things to bare in mind:

  • Keep your codebase clean. Separate what’s specific to your React/whatever user interface from the rest of your application (i.e. put the JS application code in a specific subdirectory of your Rails project)

  • Take advantage of the great tools available today such as ES6, SCSS, etc.

Webpack is here to take care of ES6 and SCSS compilation, minification, dependencies management and much more.

Webpacker is the gem that adds Webpack to a Rails project.

When you use webpacker, a new subdirectory is added to your project: app/javascript/packs/.Each pack is a module you can include in your views. Your StimulusJS application will be a module to include in your views layout (see how in the coming section).

  • Add gem 'webpacker' to your Gemfile and run bundle install.

  • Run the following command: rails webpacker:install

Take a quick look at the following files to get a sense of what’s going on:

  • config/webpacker.rb

  • config/webpack/*

  • app/javascript/packs/application.js

3. Install StimulusJS

There’s a pretty handy command to add StimulusJS to your project:

rails webpacker:install:stimulus

This will install StimulusJS, create a directory that’ll contain your controllers and add a couple of lines to your application.js pack to initialize the framework.

Take a quick look at the following:

  • app/javascript/packs/application.js

  • app/javascript/packs/controllers/

Now make Rails include your first pack in app/views/layouts/application.html.erb. If your pack is app/javascript/packs/application.js, then add the following:

Install StimulusJS

Hello world example

We’re now ready to move on to the next topic: how to use StimulusJS.

Before we get into how it works, I’d like you to read the following code snippet:

app/views/younameit/index.html.erb:

how to use StimulusJS

app/javascript/controllers/mycontroller_controller.js:

app/javascript/controllers/mycontroller_controller.js

What it does is pretty straightforward: when you click on the button, it displays “Hello World!”.

Now that you have an idea of how StimulusJS works, let’s break it all down.

Link an HTML element to a StimulusJS controller (data-controller)

The very first step is to link a part of your HTML DOM to a controller.

  1. Create a controller (see the example above or)

  2. Encompass the HTML document subpart you want to link to this controller in an element that’ll have a data-controller attribute. For instance: <div data-controller="mycontrollername">...</div>

Link an HTML element to a StimulusJS controller

That’s it. Now this div and everything in it can interact with the mycontrollername controller.

Don’t fall into this trap

If your controller’s filename contains underscores, the corresponding name to use in your HTML document has underscores (_) replaced by hyphens (-).

  • Filename: app/javascript/controllers/content_loader_controller.js (use underscores)

  • Controller name: content-loader (use hyphens) <div data-controller="content-loader">...</div>

Data binding (data-target)

This is the most basic usage of StimulusJS: reference an HTML element in a controller in order to access it later on. We call this a target (a target descriptor to be exact).

First, choose a name for each target and list them in the targets static array of your controller.

Data binding

Then add a data-target="controllername.targetname" attribute to each targeted HTML element.

Add a data-target="controllername.targetname" attribute to each targeted HTML element.

How to access a target from the controller? We do it by calling, for a target named outputthis.outputTarget.

A target named output: this.outputTarget

Since in our example output is a span, we can change its content via the textContent attribute. This means that this.outputTarget.textContent = 'Call me daddy' will display Call me daddy in the view.

One target to rule them all

While the basic use case is one target for one HTML element, you actually can have one target for multiple HTML elements. You then access them through the array this.outputTargets (plural form).

One target to rule them all
You then access them through the array this.outputTargets


Event-triggered behavior (data-action)

Want to call a method when a particular event (click, keypress, etc.) happens? Use the attribute data-action on your HTML element.

data-action, as you can easily guess, allows us to call a controller method on a certain event. In the present example:

  • The event is click

  • The controller is mycontroller

  • The method to call is say_hello

Event-triggered behavior

You can listen to many events such as click, dbclick, keyup, etc.

Initialize your controller from the view (data-*)

Sometimes you want to reuse a single controller multiple times in the same view for different elements (a different controller instance for each element).

Let’s say we have a blog with comments. Abusive comments can be reported to the administrator. Each comment shall be displayed and have its own report button. The reporting shall be asynchronous (AJAX).

It is obvious that we need only one StimulusJS controller to provide this simple behavior: catch a click event on a button and call a controller method that’ll do all the AJAX call required to report the abusive comment. We can then have each comment bound to a different instance of the controller.

Initialize your controller from the view

Since we can’t send an argument in data-action (<button data-action="click->comment-report#report(41)">...</button> doesn’t work), we need to figure out how to initialize each instance with the comment id so that the AJAX call is made to the right url.

Once again it’s really simple: when you use data-controller in an HTML element, you can also add other data-<controller name>-<attribute name> attributes. For instance data-comment-report-comment-id="42":

data-comment-report-comment-id="42"
Note that data-comment-report-comment-id uses hyphen-separated lowercase words

Note that data-comment-report-comment-id uses hyphen-separated lowercase words while this.data.get("commentId") uses lower camelCase syntax.

To sum up

There are three main tools:

  • data-controller : if your controller file is named progress_tracker_controller.js, you do <div data-controller="progress-tracker">...</div>

  • data-target : for a name target in your controller, you do <div data-controller="progress-tracker"><p data-target="progress-tracker.name"></p></div> and access it using this.nameTarget.

  • data-action : call a method rule_them_all when a click event arises by adding said method to the controller and <div data-controller="progress-tracker"><p data-action="click->progress-tracker#rule_them_all">Click me</p></div>

There are also these 3 methods that I’m not going to cover in this article, just note that they exist and can come in handy once you get familiar with StimulusJS and need to do some more advanced stuff.

  • initialize()is invoked once, when the controller is first instantiated.

  • connect(): is invoked anytime the controller is connected to the DOM.

  • disconnect(): is invoked anytime the controller is disconnected from the DOM.

From here, you can do pretty much anything you want. You can import JQuery in your controller and do AJAX calls, change the style of an element, etc. You basically delegate the bindings/events management part to StimulusJS and do the rest with your tools of choice. You can even skip the target part and use JQuery selectors but why bother when StimulusJS does it for you?

Bonus: Add StimulusJS to ActiveAdmin

If you need to provide a better user experience on your ActiveAdmin dashboard, I suggest you add a little sugar to it using Stimulus. The principle is the same: views, controllers, data-*.

We’ve seen how to include a pack in a view layout using javascript_pack_tag, but how do you include then in ActiveAdmin?

Open config/initializers/active_admin.rband add the following:

config/initializers/active_admin.rb

That’s it for the configuration part!

Here’s a basic example of how to use it in a form:

Basic example
how to use it in a form

One thing you might notice is that you need to restart your Rails server for any changes in the controller to be taken into account. This is because the webpack compilation is done only once, at application startup.

One way of fixing this is to remove the lines added to config/initializers/active_admin.rb and simply add script src: asset_pack_path('application.js') to each ActiveAdmin form that uses a StimulusJS controller:

Add script src: asset_pack_path('application.js')


Clone this Github repository and start playing with StimulusJS!

Thanks for reading!

Partager
Younes Serraj
Younes SerrajOct 23, 2018

Capsens' blog

Capsens is an agency specialized in the development of fintech solutions. We love startups, scrum methodology, Ruby and React.