Trying out Stimulus

DDH shared a tweet recently talking about a new version for a tool called Stimulus.

https://stimulusjs.org/handbook/hello-stimulus

I was looking for a leaner stack to build an interactive guide on tickingfocus.com.

You can access it here

Creating Interactive Page

I got this static page, markdown compiled and templated to a nice static website, thanks for Hugo. Now I wanted to make one of these pages interactive, add buttons, forms, etc.

Using a full-featured react or nextjs app made no sense, I’d have to rebuild the whole website, make sure there where no outdated links, the i18n was working, etc. I like Hugo and I’m productive with it now. Why change and make my stack complex?

I could build the interactive part in a fancy tool like create react app and include it as a piece of javascript. But that’s something I still need to learn to do. I haven’t looked into packaging typescript+react app to embed on a webpage. And I can tell this might not be fun either: how to share the CSS between projects? re-release two projects on every layout change? Nah.

I could go old school and use some javascript to make my tutorial interactive.

That’s where Stimulus looked useful. It’s the missing link between HTML and SPAs.

Why not go “raw javascript” completely? Still an open question. I think having a framework makes my life easy, and stops me from inventing mine.

Stimulus

Basecamp built this. successful company, with good values. I drink the kool aid, read all of their books.

Installing the library is rather involved: https://stimulusjs.org/handbook/installing

Not sure this is useful for now. You can include the script and load your controller manually. That’s good enough.

That’s what I did in my Hugo website, I just write the whole interactivity page INSIDE the markdown file. Five years ago I might have judged myself for being a script-kiddy, but it looks like a nice balance of value over setup time now.

I think you could summarize this tool with “we love HTML, js, and CSS, and we designed the perfect way to weave them”.

This is VERY different from my “regular” stack that I could summarize with “we hate HTML, js, and CSS, so we designed the perfect way to rewrite them all.”

Quickstart

Controller connects HTML.

data-controller="controllerName" will create an instance of the controller on a given piece of HTML.

data-controllerName-target="fieldName" will connect the controller with this HTML element.

  • use static targets = ["fieldName"] in the controller class
  • use this.{fieldName}Target to access the element.

data-action="controllerName#copy will connect the click action (implicit) with the controller method. data-action="action->controllerName#copy is the full definition.

Default events: https://stimulusjs.org/handbook/building-something-real#common-events-have-a-shorthand-action-notation

    // src/controllers/hello_controller.js
    import { Controller } from "stimulus"
    
    export default class extends Controller {
      connect() { // triggered when the controller is bound to the document.
      	this.element // element connected
      }
    }

data-controllerName-fieldName-value="1" will connect the html value with the controller’s field. You have to type it: static values = {fieldName: Number} then you can set/get using this.{fieldName}Value.

You can define observers on values: fieldNameValueChanged()

You can use multiple data-action for a single element, use a space-separated list.

reference for actions

global actions: resize@window. You can add options: open:once

You can define multiple controllers with space-separated lists too: <div data-controller="clipboard list-item"></div>

Resilient Design

That sounds amazing. In practice, they hide the element until we add “feature-xx-supported” classes. Why not add these to the body directly?

State

Big topic, here everything is in HTML. That’s weird when you come from React where state libraries are an unending work in progress. Their example is a slideshow, similar to what I want to build, perfect.

The example is stateful, you have to call an old school “showCurrentSlide” to update the rendering after state change. That looks antic.

They use the DOM directly

I like that. Having worked in so high levels framework, touching low level JS and DOM operations is the sign that it’s really a sane middle ground between approaches.

After trying out with my interactive guide, I used patterns like this one:

.first-session--success, .first-session--fail {
    display: none;
}
[data-guide-first-session-value="C"] .first-session--fail {
    display: inline-block;
}
[data-guide-first-session-value="X"] .first-session--success {
    display: inline-block;
}

That data-guide-first-session-value="X" is just a regular HTML field, I could load this from pretty much anywhere. Here, it’s a default coming from the static page that gets changed by Stimulus as we go through the guide.

We can use regular css selectors like these (ugly but efficient) to create interactivity quickly.

Lifecycle

Lifecycle: https://stimulusjs.org/handbook/managing-state#lifecycle-callbacks-explained

That part I don’t get the difference between controller / instance / class. Can you have the same controller twice in the page? Is the initialization code called twice? What about connect?

This might get the job done, but it’s clunky, requires us to make a decision about what to name the attribute, and doesn’t help us if we want to access the index again or increment it and persist the result in the DOM.

This needs to be discussed. So many questions look trivial to me, I’m missing something.

Reference

https://stimulusjs.org/reference/controllers

Registering controllers by hand: (no build step) https://stimulusjs.org/reference/controllers#registering-controllers-manually

https://stimulusjs.org/reference/lifecycle-callbacks#reconnection

That one is magic, how do you reconnect a controller? How do you tell that it’s a controller to reconnect or a new controller to connect? There is some internal state? Is it just like a memory pool?

Tricks

<div data-controller="search">
  <div data-controller="search">
    <div data-search-target="results"></div>
  </div>
</div>

what happens with the target?

https://stimulusjs.org/reference/targets#properties

beautiful.

CSS

https://stimulusjs.org/reference/css-classes#properties

That one I don’t get why not make it easier. There is a this.hasNAMEClass, why not have a setter to toggle the class? The example forces you to go this.element.classList.add(this.NAMEClass).

OK, it makes sense if you want to apply the class to a child element. Toggling there would be weird. this.childElement.NAMEClass = true, lots of implication there.