Skip to main content
Javascript and CSS Asset Pipeline:

Stimulus JS Framework Introduction

41

Episode 225 · January 23, 2018

A look into Stimulus JS, a new Javascript framework by Basecamp to pair closely with Turbolinks

Javascript


Transcripts

What's up guys? Welcome back, we are going to dive into the Stimulus JavaScript framework this episode, I'm going to give you an introduction to show you how it compares to other JavaScript framewoks and libraries, and then we'll dive into taking a look at some examples, but then, in a future episode, we'll dive into some more complex stuff. This is just your primer, your introduction, we're going to take a look at all of that in this episode. Where does Stimulus fit in? and why do we need another JavaScript framework? and why are you calling it a framework in quotes? Well, Stimulus is very different from what you might expect, so when you think of a JavaScript framework these days, you think of something that helps you rendering HTML, handling events, worrying about your state, doing all kinds of other things like sub components and organizing all your code and all stuff, well it's not really what Stimulus does, it does some of that, but it's designed mostly to just handle events in an organized manner, and so we have to first take a look at where Basecamp has come and their approach on JavaScript on the front end, and so we'll jump back to turbolinks real quick and talk about Turbolinks and why it exists.

There was a time when everybody was talking about how fast it was to build your front end in JavaScript and render out all of the HTML and that was way faster than browsers being sent a GET request, having to reload all of your JavaScript and CSS and making those pages slow just because the browser would clear out all of that on the next request, and so Turbolinks was introduced to give you the speed of a single page app without any of the complexity, so all it does is listen to link clicks and say: Well, instead of doing that with the regular browser process, let's just do that with AJAX, let's replace the DOM with a new DOM, and rerun your JavaScript as necessary, and that is that. That worked out really nicely in a lot of cases, but it didn't address the entire problem that other front end frameworks and single page app frameworks did, so those took care of helping you render HTML, they took care of the state, they did a lot of extra stuff and so that is where Turbolinks fell flat, it only made new page request quick but you were still required to use something like vanilla JavaScript or jQuery to handle events to build out complex forms or wizards or anything like that that might be interactable in a page, and so Stimulus is being introduced as an alternative to doing your front-end framework as a framework again, and you don't have to go through and come up with your own structure for vanilla JavaScript, you don't have to use jQuery either, and so this is designed more as a competitor to jQuery more than is a competitor to Ember, Angular, React or Vue. Those are all still fairly heavy front end things, and Stimulus and Turbolinks are designed to do one thing and do one thing really well, and they kind of fit really well together because they don't step on each other's toes or anything like that, they do very separate things and so Stimulus is mostly designed to help take events that happen on your HTML and then run some JavaScript and basically you just define data attributes and where jQuery might have said: Well, we need to look up this element if it's on the page then we need to connect this event listener and all that. Those things are taken care of for you in Stimulus, and all you do is write some data attributes like data controller and data target and data action and those will be wired up for you and all you have to do is implement the equivalent methods inside of your controller to make those functions work, and so this is kind of like building out your jQuery events in a much more structured way and so you're unlikely to get as much spaguetti JavaScript code as you might where you accidentaly do things in jQuery and don't structure them correctly, so this is very much a competitor to jQuery more than it is with Vue or Angular or React or Ember, and that's something important to keep in mind because I think a lot of people read this and they're like: Does it do AJAX requests for me? Does it handle state? Does it render HTML? NO Pretty much none of that, it just really focuses on events because you also have rails Vuejs to do your AJAX requests, that's built in, you have Turbolinks to make your page render the new content or whatever, and so Stimulus is just designed to say: Well, we have this HTML and we want to make it interactable, how do we do that? And so that is where Stimulus fits in.

Let's dive into and example application, and let's generate a new app. We're going to use webpacker to install Stimulus, so let's just call this

rails new stimulating --webpack

You can always go to the webpacker installation instructions and add webpacker to an old application if that's what you would like to do. This will just go ahead and install it automatically for us so we don't have to worry about that. Once this is set up, we can then go and create our Stimulus set up code inside code inside of our JavaScript pack tag and all of that, so we'll get a Stimulus set up and then we'll be able to use that anywhere in our application. So if we go into this stimulating ap we can

cd stimulating
yarn add stimulus

That's going to install the npm package for us, and then we can open up our application here, we're going to need to go and do one thing first, we're going to go to the application.html.erb, and we'll grab this JavaScript include tag and change it to a pack tag. That will load our app/javascript/packs/application.js, now we don't need any of this in here really, but wo do need to set up our initalizer code for stimulus, so let's take a look at that. We need to import

import { Applicatoin } from 'stimulus'
import { autoload } from 'stumulus/webpack-helpers'

const application = Application.start()
const constrollers = require.context("./controllers", true, /\.js/ )
autoload(controllers, application)

What that's going to do is require us to have a app/javascript/packs/controllers folder, you cal also do ../controllers if you wanted app/javascript/controllers, but we're gonna do that just in the standard folder, so we'll have controllers here, and that folder will show up in a second once we add app/javascript/packs/controllers/hello_controller.js so you need to name it with the same name and underscore like you would with rails, and then underscore controller.js at the end, so this is very similar to how you do a controller file name in rails, and then the difference once you create one of those is that you're going to create a class here and export that. So here first we need to

import { Controller } from 'stimulus'

export default class extends Controller {}

So I have an unnamed class that inherits from controller and then we're going to export this so that when the autoload loads it it will be able to get this class and use access to that somewhere else, and in here we can do all of our actions that we would like, so we don't have any pages in our rails app yet, so let's go create one.

rails g scaffold Event name

let's leave it at that for now and we'll go back in another episode and so some more complex stuff with Stimulus, with this done, let's run

rails db:migrate

Let's go to our rails app and go to the routes file and sert

root to: 'events#index'

Save that, and let's go to the events index.html.erb and in here is where we can begin by adding our Stimulus stuff, so let's just create a new tag here at the bottom

<div data-controller="hello">
  <input data-target="helo.name">

You might think that you could just type name here like you would probably do on your own, but you actually need to namespace this under the controller name for it to work properly, and so keep that in mind, but that's going to help in case you mix these in with other ones, you'll know that this is the name for the hello controller, and you might have html mixed in with another component that has a name as well, and so that way it doesn't get confused.

<div data-controller="hello">
  <input data-target="helo.name" type="text">
  <button data-action="click->hello#log">Log</button>
</div>

This one syntax is very simple, you have the event name similar to all the event names that you would listen to like on submit or on click or on paste or key up, any of those things you can do and then you can tell it with the arrow, call this controller and this action, so we're going to have methods in here that will match those action names, and so then from here, you have access to a special variable called this.targets, that is coming from your controller, which will allow you to grab those targets and you can say

export default class extends Controller {
  log() {
    this.targets.find("name")
  }
}

that will look for anything called "Hello.name", and grab that first one, and then we can ask for the value on that, we can say console.log that out. If we refresh our page, we should be able to see this, and we should be able to say "Test 123", click "Log", and that will print that out.

That worked well, but I want to point out that this works for all events, by default, click is kind of the most common one, a lot of times we need to use that, but if you had a form here, you could actually apply this stuff to rails form, and you could have a data action submit, and you could have a call some JavaScript in your controller when the submit was attempted, and so you could check for validations then and you could cancel the "Submit" if you wanted to, you could do all that kind of stuff, and I'm going to show you how you can interact with the event when you submit something, so for example, instead we don't have a form here, instead let's create a data action on the input element, we'll say: Well, when you paste into this input element, we'll call the hello paste method, and our paste method is just going to be one of those annoying ones that says

paste (event) {
  event.preventDefault()
}

that will happen because we can receive the event as an argument to our action here, so for all of these if you ever want to interact with that, just type event here, or "e", and that will make sure that you get access to that, otherwise it just sends it anyways, but you ignore it and don't save it to a variable, so you can always add that in, and then call preventDefault if you want to intercept and cancel that from happening. From here we could just say console.log("pastes are not allowed"), and so if we grab some text here, copy it to the clipboard, and we refresh our browser, we should be able to paste that in, and it will say: "Pastes are not allowed" and it didn't actually put the text into the box either because the preventDefault stopped it from doing that. That's cool, that's how you could add this to your forms, and then go check all of those data targets, and say: Does this match? Is this filled out or not? Yes or no? Does this match the REGEX that I wanted to match or whatever, all those kind of validations you can do really easily with something like this which is really cool. Last but not least, we want to talk about state in these components, because they are very different than the state you might think of in React or Vue where you have a JSON object doing that, in Stimulus they encourage you to use html attributes to do that. For example, if you wanted to pass in a default value for the name, you would do something like data-hello-name="Chris", and here we would be able to access, we could

get name() {
  if(this.data.has("name")) {
    return this.data.get("name")
  } else {
    return "Default User"
  }
}

We could return this value on there, and so we'll either get the default name or not, and we can define an initialize method here, and this is going to allow us to say:

initialize() {
  this.nameElement.value = this.name
}

That's going to set the default value when we load our page. So loading our page we see that we get "Chris" as the default value in our log box, we can click log, but that's not going to work anymore, because we need to use this.name.element to access the getter, and then if we go and remove the default value there, we can refresh this page, and it's going to say: "Default user" instead, and log in should work now, if we type "Chris" in there, we can type "Log", or hit "Log" and that print out the value that is currently in there.

All of this is kind of designed so that your HTML is what keeps track of the state, and you're going to use that as a place that you can have so that when Turbolinks reloads the cached version of the page, the stuff will continue to work, you're not making extra AJAX requests, just to fill out the form because rails can go ahead and add that data attribute in with JSON or text or whatever values you need, and then your page can immediately be functional as soon as it gets rendered in your browser that way you're not making these extra AJAX requests that make certain widgets of your page load after the page is loaded, which gets kind of frustrating, so this takes an angle at things that's different than your JavaScript frameworks then you're used to, and I really like this for simple stuff, I'm very curious what happens as you go and build more complex things witht this, I think it will work out pretty well, but I haven't built anything super complicated with it yet, and so that is what we're going to be diving into more in the next few weeks as I learn this more and we get to see what other people are building with it. I think it will work out pretty well, and the simplicity of this goes hand in hand with the simplicity of rails, if you know how rails controllers and routes work, this all feels very similar to how those work just in JavaScript land instead, so it's really cool, the one thing I keep forgetting is these namespaces on the targets and the data attributes, I always kind of forget that they need to have the namespace of the controller in there, but so far that's not really that bad. That is it for this episode, I hope that helped you wrap your head around Stimulus and where it fits in and what it's good at and what it's not good at, and hopefully that will make it easier for you to decide if you want to use Stimulus or not in the future. If you have any questions, as always let me know in the comments below and I'll do my best to help answer those and get you a better understanding of Stimulus js. Until next episode, I will talk to you later. Peace

As a recap, here's the complete app/javascript/packs/controllers/hello_controller.js

import { Controller } from "stimulus"


export default class extends Controller {
  initialize() {
    this.nameElement.value = this.name
  }

  log(event) {
    console.log(this.nameElement.value)
  }

  paste(event) {
    event.preventDefault()
    console.log("pastes are not allowed")
  }

  get name() {
    if (this.data.has("name")) {
      return this.data.get("name")
    } else {
      return "Default User"
    }
  }

  get nameElement() {
    return this.targets.find("name")
  }
}

Transcript written by Miguel

Loading...

Subscribe to the newsletter

Join 18,000+ developers who get early access to new screencasts, articles, guides, updates, and more.

By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

More of a social being? We're also on Twitter and YouTube.