11/06/2020

Stimulez vos pages Rails avec Stimulus

Rails
Ruby
Stimulus

On connaît la capacité de Rails à gérer simplement les base de données qui se cachent derrière les applications les plus complexes et sur lesquels s'appuient les process métiers. Avec l'expérience, cette complexité peut assez facilement être domptée avec l'aide d'ActiveRecord, d'ActiveJob et d'ActiveStorage.

Mais dès qu'il s'agit de remonter dans les couches ISO et de s'occuper de la plus haute, la présentation, on est un peu paumé si le client demande des interactions fluides (sans rechargement de la page) à la mode comme avec les applications React. C'est là que le casse-tête commence... Que faire ? Apprendre React et développer la partie frontale 'le Client' et reléger Rails à fournir su JSON au front et gérer le backend ?

Ce dilemme n'est plus depuis la V6 de Ruby on Rails qui épouse et embrasse webpacker, ce qui va nous permettre d'intégrer facilement des outils comme React ou Stimulus, et ainsi rendre interactives nos pages Rails sans se prendre la tête avec les multiples bibliothèques JQuery intégrées aux forceps, comme par le passé.

Aujourd'hui c'est donc de Stimulus que je vais vous parlez.

Il a été conçu par l'équipe Basecamp à qui l'on doit la constante évolution de Rails, grâce au cercle vertueux Produit <=> Framework et à ses 3 millions d'utilisateurs.

Stimulus permet de rendre de petits services mais n'est pas comparable à React car son domaine de prédilection est réduit à la page qu'il contrôle et il ne permet pas de créer de composant, ni d'application.

La contre-partie est qu'il est simple à installer, à comprendre, et donc à prendre en main.

Prenons un exemple pratique; vous affichez une liste d'éléments et vous voudriez que l'utilisateur puisse en sélectionner certains éléments et afficher alors une liste déroulante proposant des actions à faire subir à cette collection d'éléments cochés, comme par exemple 'Envoyer', 'Supprimer', etc...

Pour ce faire nous allons utiliser un contrôleur nommé 'action', une action 'click' et deux 'targets'; la cache à cocher qui sélectionne un élément ('source')et la liste des actions possibles ('selector').

C'est parti !

(Vérifiez que l'application Rails sur laquelle vous travaillez est bien en version 6.0.x, sinon ce n'est pas la peine d'aller plus loin.)

On installe Stimulus comme suit :

$ rails webpacker:install:stimulus

Puis on ajoute notre controlleur dans app/javascript/controllers (ex: action_controller.js)

import { Controller } from "stimulus" export default class extends Controller { static targets = [ "source", "selector" ] initialize() { this.selectorTarget.style.visibility = "hidden"; } connect() { console.log("Hello, Stimulus!", this.element) } click() { var check_boxes = this.sourceTargets; var enabled = check_boxes.filter(myFunction); function myFunction(value, index, array) { return value.checked; } if (enabled.length == 0) { this.selectorTarget.style.visibility = "hidden"; } else { this.selectorTarget.style.visibility = "visible"; } } }

Dans la vue, on ajoute cette directive qui a pour but de déclarer que cette région est maintenant contrôlée par le bien nommé controlleur 'action'. Notez que comme plusieurs controlleurs peuvent être actifs dans une même région, on ajoutera le nom du controlleur devant le nom de l'élément utilisé par ce controlleur. ex: 'action.source'

Dans le 'partial' qui affiche l'élément de la collection à afficher, ici une facture, on ajoute la case à cocher qui contient l'action:

<%= check_box_tag "[ids][#{facture.id}]", 'yes', false, data: {"target":"action.source"} %>

En fin de région controllée, ajouter le selecteur d'actions

<%= select_tag :action_name, options_for_select(['Envoyer', 'Supprimer']), include_blank: true, onchange: 'this.form.submit()', data: {"target": "action.selector"} %>

Et voilà, le tour est joué !

La liste des actions est cachée au chargement de la page et ne s'affiche que s'il y a au moins un élément coché.

On voit ici toute la simplicité et l'intérêt de Stimulus, qui s'intègre très naturellement dans du code existant.