Une intro au framework StimulusJS.
Stimulus a été créé par Basecamp. C'est un framework de front, plus simple à prendre en main qu'Angular, React ou Vue. Il n'a pas été conçu pour rendre du HTML, mais pour l'augmenter.
StimulusJS fonctionne selon le principe de separation of concerns: on segmente l'application en plusieurs parties afin que chacune d’entre elles isole et gère un aspect précis. De la même façon qu'on sépare le contenu de la présentation avec le CSS, Stimulus permet de séparer le contenu du comportement. Cet arrangement aide à construire des composants réutilisables.
Utilisez ce tutoriel
Nous allons commencer par un exemple simple d'implémentation :)
Nous allons utiliser webpack, qui nécessite une version minimum de Rails 5.1
Vous pouvez vérifier votre version de rails en faisant rails --version
Pour installer une version supérieure, faire: gem install rails -v 5.2.1
rails new my_app
cd my_app
Rendez-vous sur la page d'installation de yarn, choisissez votre version et suivez les instructions.
Nous allons installer webpacker qui permet d'ajouter webpack à un projet Rails. Stimulus fonctionne avec webpack pour charger automatiquement les fichiers de controllers dans l'app.
Ajoutez à votre Gemfile:
gem 'webpacker'
bundle install
rails webpacker:install
Plein de fichiers se sont créés ! Nous allons voir rapidement ce qui s'est ajouté:
- dans app, il y a un nouveau dossier javascript/packs. Chaque pack est un module que vous pouvez ajouter dans les views Nous allons voir ça plus bas.
- config/webpack
- dans config, webpacker.yml
- babel.config.js, postcss.config.js, yarn.lock, .browserslistrc
- bin/webpack, bin/webpack-dev-server
yarn add stimulus
(ou npm)
rails webpacker:install:stimulus
rails webpacker:install:stimulus
Cette commande va:
- ajouter stimulus à votre fichier package.json
- créer un dossier app/javascript/controllers qui va contenir vos controllers
- ajouter l'import des controllers dans votre fichier app/javascript/application.js
Ajouter dans le head dans app/views/layouts/application.html.erb la ligne suivante:
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload', async: true %>
Nous allons tester pour la première fois l'implémentation de StimulusJS dans votre application 🎉
rails g controller home index
Dans config/routes.rb, indexez la home page à votre controller home :
root 'home#index'
- Un petit bonjour
Dans app/views/home/index.html.erb nous allons commencer par du HTML simple :
<div>
<input type="text">
<button>Greet</button>
</div>
Vous allez maintenant créer votre premier controller Stimulus dans le fichier app/javascript/controllers/hello_controller.js:
import { Controller } from "stimulus"
export default class extends Controller {
}
Le principe même de Stimulus est de connecter automatiquement les événements DOM aux controllers Stimulus qui sont des objets JavaScript.
Maintenant nous allons placer un identifiant data-controller à notre div.
<div data-controller="hello">
<input type="text">
<button>Greet</button>
</div>
Stimulus fonctionne en monitorant la page, attendant que l'attribut
data-controller
apparaisse.
data-controller connecte et déconnecte les controllers Stimulus. Vous pouvez vous le représenter comme ça: class fait le lien entre HTML et CSS, data-controller fait le lien entre HTML et JavaScript.
Pour vérifier que votre HTML est bien connecté à votre controller Stimulus nous allons ajouter ces lignes au controller:
import { Controller } from "stimulus"
export default class extends Controller {
connect() {
console.log("Hello, Stimulus!", this.element)
}
}
La méthode
connect()
est appelée par Stimulus à chaque fois qu'un controller est connecté au document.
C'est le moment de le voir en action !
rails s
Rendez-vous sur http://localhost:3000/
et ouvrez votre console :)
- Une action qui répond aux évenements DOM
Maintenant que votre page vous a bien indiqué qu'elle était "connectée" avec Stimulus, vous pouvez changer le nom de votre méthode comme ceci:
import { Controller } from "stimulus"
export default class extends Controller {
greet() {
console.log("Hello, Stimulus!", this.element)
}
}
Pour connecter notre bouton à notre méthode, nous allons utiliser l'attribut data-action comme ceci dans notre HTML:
<div data-controller="hello">
<input type="text">
<button data-action="click->hello#greet">Greet</button>
</div>
data-action
décrit comment les événements de la page doivent déclencher les méthodes de controllers.
Sa valeur click->hello#greet est appelée un action descriptor.
Ici ce descriptor indique que "click" est le nom de de l'événement, "hello" est le nom du controller et "greet" le nom de la méthode à déclencher.
Rechargez votre page et cliquez sur le bouton, n'oubliez pas de regarder dans votre console !
- Hello, you <3
Nous allons maintenant faire en sorte que notre action permette de dire bonjour au nom rentré dans l'input.
Nous allons modifier notre HTML comme ceci:
<div data-controller="hello">
<input data-target="hello.name" type="text">
<button data-action="click->hello#greet">Greet</button>
</div>
data-target
permet de marquer les éléments importants comme target pour qu'on puisse faciliment les référencer dans le controller.
Sa valeur hello.name est appelée un target descriptor.
Ici ce descriptor indique que hello est le controller et que name est le name de la target.
On modifie ainsi notre controller:
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "name" ]
greet() {
const element = this.nameTarget
const name = element.value
console.log(`Hello, ${name}!`)
}
}
Vous avez pu voir que nous avons ajouté la ligne :
static targets = [ "name" ]
Pour chaque target présente dans l'array, Stimulus ajoute trois nouvelles propriétés à votre controller:
- this.nameTarget: renvoie le premier élément target qui correspond au nom présent dans le scope du controller et dont on peut alors récupérer la valeur. S'il n'y en a pas ça renvoie une erreur;
- this.nameTargets: renvoie un array d'éléments correspondants dans le scope du controller;
- this.hasNameTarget: renvoie true ou false selon s'il y a une target présente ou non
Rechargez la page, cliquez sur le bouton et regardez votre console.
- Un form complet
Nous allons finir en faisant en sorte que le message apparaisse sur la page et non dans notre console !
Pour cela on va ajouter une div où on veut que notre nom apparaisse, avec une data-target avec comme targe descriptor hello.output.
<div data-controller="hello">
<input data-target="hello.name" type="text">
<button data-action="click->hello#greet">Greet</button>
<div data-target="hello.output"></div>
</div>
On va enfin modifier notre controller comme ceci:
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "name", "output" ]
greet() {
const element = this.nameTarget
const name = element.value
this.outputTarget.textContent =
`Hello, ${name}!`
}
}
Rechargez la page et cliquez sur le bouton.
- Bonus: un peu de refactoring, déjà !
Nous pouvons nettoyer un peu notre méthode greet() en extrayant la récupération de la valeur de name.
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "name", "output" ]
greet() {
this.outputTarget.textContent =
`Hello, ${this.name}!`
}
get name() {
return this.nameTarget.value
}
}
Pour la suite de cet atelier, je vous propose plusieurs idées selon à quel point vous vous sentez maintenant à l'aise avec StimulusJS:
- vous pouvez continuer les tutos de StimulusJs, qui vous proposent soit de construire un bouton qui permet de copier du texte ou un slideshow
- vous pouvez faire un petit exercice sans guide, en faisant un quizz, en vous inspirant de cet exemple
Quelques liens utiles si vous voulez creuser le sujet:
- Les origines de Stimulus
- resources: des définitions plus précises des éléments et méthodes
- Awesome Stimulus: regroupement de nombreux projets en open source faits avec Stimulus et aussi de nombreux tuto, une véritable mine d'or !