Skip to content

Instantly share code, notes, and snippets.

@mrmartineau
Last active April 19, 2024 09:41
Show Gist options
  • Save mrmartineau/a4b7dfc22dc8312f521b42bb3c9a7c1e to your computer and use it in GitHub Desktop.
Save mrmartineau/a4b7dfc22dc8312f521b42bb3c9a7c1e to your computer and use it in GitHub Desktop.
Stimulus cheatsheet

Stimulus cheatsheet

Lifecycle callbacks

  • initialize: once, when the controller is first instantiated
  • connect: anytime the controller is connected to the DOM
  • disconnect: anytime the controller is disconnected from the DOM

Action descriptors

The data-action value click->hello#greet is called an action descriptor. This particular descriptor says:

  • click is the event name
  • hello is the controller identifier
  • greet is the name of the method to invoke

Common Events Have a Shorthand Action Notation

Stimulus defines click as the default event for actions on <button> elements. Certain other elements have default events, too. Here’s the full list:

Element > Default event

  • a > click
  • button > click
  • form > submit
  • input > change
  • input > type=submit click
  • select > change
  • textarea > change

Multiple Actions

If an element has muliple actions you can separate each one with a space click->hello#greet click->hello#save.

The element can even have multiple actions for multiple controllers click->hello#greet click->history#save.

Targets

The data-target value hello.name is called a target descriptor. This particular descriptor says:

  • hello is the controller identifier
  • name is the target name

When Stimulus loads your controller class, it looks for target name strings in a static array called targets. For each target name in the array, Stimulus adds three new properties to your controller. Here, our source target name becomes the following properties:

  • this.sourceTarget evaluates to the first source target in your controller’s scope. If there is no source target, accessing the property throws an error.
  • this.sourceTargets evaluates to an array of all source targets in the controller’s scope.
  • this.hasSourceTarget evaluates to true if there is a source target or false if not.

Multiple Targets

If an element is a target for multiple controllers you can separate each one with a space hello.name history.text

Naming Conventions

Component Convention Rationale
Controller filenames snake_case.js Rails works that way
Identifiers kebab-case Sometimes used as part of HTML attribute names; analogous to CSS classes, which are conventionally kebab-case
Action names camelCase Map directly to JavaScript controller methods
Target names camelCase Map directly to JavaScript controller properties
Data attributes camelCase + kebab-case Thin wrapper around the HTMLElement.dataSet API; camelCase names in JS, kebab-case attributes in HTML

Data API

Each Stimulus controller has a this.data object with has(), get(), and set() methods. These methods provide convenient access to data attributes on the controller’s element, scoped by the controller’s identifier.

For example, in our controller above:

  • this.data.has("index") returns true if the controller’s element has a data-slideshow-index attribute
  • this.data.get("index") returns the string value of the element’s data-slideshow-index attribute
  • this.data.set("index", index) sets the element’s data-slideshow-index attribute to the string value of index

If your attribute name consists of more than one word, reference it as camelCase in JavaScript and attribute-case in HTML. For example, you can read the data-slideshow-current-class-name attribute with this.data.get("currentClassName").

HTML API

Controller data-controller

e.g. <div data-controller="ControllerName">

e.g. Multiple controllers on the same element: <div data-controller="ControllerName AnotherControllerName">

Target data-target

e.g. <input data-target="ControllerName.TargetName">

Action data-action

e.g. <button data-action="eventName->ControllerName#methodName">Click me</button>

e.g. Multiple actions on the same element: <button data-action="eventName->ControllerName#methodName anotherEventName->ControllerName#anotherMethodName">Click me</button>

Code snippets

Empty class

import { Controller } from "stimulus"

export default class extends Controller {
  static targets = []

  // or
  static get targets () {
    return []
  }

  initialize () {}

  connect () {}

  disconnect () {}
}

HTML

Example HTML from the Stimulus home page

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

  <button data-action="click->hello#greet">
    Greet
  </button>

  <span data-target="hello.output">
  </span>
</div>
@niklasnson
Copy link

Nice work, thanks!

@jmas
Copy link

jmas commented Aug 3, 2018

Thanks!

@richjdsmith
Copy link

This is much better than sorting through the "handbook"

Thanks for it!

@DerDu
Copy link

DerDu commented Jan 5, 2021

Quick question (from "Es6-class-export-import-module"-stuff-noob)

I'm using Symfony/Webpack/Encore/Stimulus, got it running nicely, and got some classes/modules.
Everything is hooked together in my main app.js (e.g. create object instances and init them, like a websocket connection pool)
Now there is this stimulus controller containing a method executed by magic when clicking a button - fine with me and working so far :)

What i want this method to do is:

  • Call a method on an object i already have with data collected by stimulus (e.g. send chat message from input field)

So how may i inject the existing object into this controller thingy for usage?
(Currently my only option is clutter global with my object)

How does one do this correctly?

(And i'am aware that i may miss something with es6, i come from a land aout of time, where netscape navigator was living ;-) )

@n4cr
Copy link

n4cr commented Jan 10, 2021

@DerDu I believe you should import that object in your controller file and then access it within the controller and your parameters.

@crimson-knight
Copy link

@DerDu I think the "stimulus" way to do this would be to make your controller wrap the chat area and in the action you trigger from the click event, you would THEN collect the data and send it. There isn't really a need to already have another javascript object outside of the stimulus controller in this example. I actually do this for my own project, and I use the HTML on the page as an "object" to fetch the attributes from for sending/receiving messages. it works really well, and it's pretty minimal.

@pySilver
Copy link

Great summary!

I'm not sure if data-target="hello.name history.text" is future-proof since official documentation does not mention this syntax (anymore?). Same goes with Data API they probably wants us to use Values instead.

@mrmartineau
Copy link
Author

@pySilver I haven't used Stimulus for about 5 years now so I'm sure the docs here are out of date..

@gap777
Copy link

gap777 commented Mar 31, 2023

WOW. This is super helpful. Didn't know about the data api at all!

@zernel
Copy link

zernel commented Sep 27, 2023

May I ask where the syntax "data-target" comes from? I couldn't find any relevant content in the official documentation.

@zernel
Copy link

zernel commented Sep 27, 2023

        deprecate(element, targetName) {
            if (element) {
                const { identifier } = this;
                const attributeName = this.schema.targetAttribute;
                const revisedAttributeName = this.schema.targetAttributeForScope(identifier);
                this.guide.warn(element, `target:${targetName}`, `Please replace ${attributeName}="${identifier}.${targetName}" with ${revisedAttributeName}="${targetName}". ` +
                    `The ${attributeName} attribute is deprecated and will be removed in a future version of Stimulus.`);
            }
            return element;
        }

Looks like this syntax had been deprecated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment