L'API Values : typer l'état d'un controller
Passé les premiers data-controller et data-target, la vraie puissance de Stimulus tient dans son API Values. Elle transforme les attributs data-* en propriétés JavaScript typées, avec sérialisation/désérialisation automatique et un callback déclenché à chaque changement.
// controllers/slider_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = { index: { type: Number, default: 0 }, autoplay: Boolean }
static targets = ["slide"]
indexValueChanged(value, previousValue) {
this.slideTargets.forEach((el, i) => {
el.classList.toggle("is-active", i === value)
})
}
next() {
this.indexValue = (this.indexValue + 1) % this.slideTargets.length
}
}
Le HTML reste déclaratif : data-slider-index-value="2" ou data-slider-autoplay-value="true" suffisent à initialiser l'état. Le callback indexValueChanged évite d'avoir à relire le DOM manuellement à chaque interaction.
Callbacks de cycle de vie sur les targets
Depuis Stimulus 3, chaque target déclenche aussi ses propres hooks : slideTargetConnected(element) et slideTargetDisconnected(element). C'est précieux avec Turbo Streams, où des éléments apparaissent et disparaissent dynamiquement sans rechargement complet du controller.
Outlets : faire communiquer plusieurs controllers
Avant les outlets, faire dialoguer deux controllers passait souvent par des CustomEvent bricolés. L'API Outlets formalise cette relation : un controller déclare une dépendance vers un autre via static outlets = ["modal"], reliée dans le HTML par data-slider-modal-outlet=".modal".
static outlets = ["modal"]
open() {
this.modalOutlet.show()
}
modalOutletConnected(outlet, element) {
outlet.registerTrigger(this)
}
Chaque outlet expose l'instance du controller cible (avec accès direct à ses méthodes publiques) ainsi que son élément DOM, et déclenche ses propres callbacks Connected/Disconnected quand la cible entre ou sort du document.
Actions avec paramètres
Autre point souvent sous-utilisé : les action parameters. Plutôt que de relire un data-id à la main dans le handler, on le déclare directement sur l'élément déclencheur : data-action="click->slider#goTo" data-slider-index-param="3", récupéré ensuite via goTo({ params: { index } }).
Bonnes pratiques à garder en tête
- Un controller = une responsabilité ; préférez plusieurs petits controllers reliés par outlets plutôt qu'un god-controller.
- Utilisez l'API Classes (
static classes = ["active"]) pour éviter de coder en dur des noms de classes CSS. - Ne dupliquez pas ce que Turbo gère déjà (navigation, remplacement partiel) : Stimulus doit rester la couche comportementale, pas un mini-framework SPA.
- Préfixez les méthodes internes non destinées aux actions pour clarifier l'API publique du controller.