Buy Access to Course
07.

Stimulus (estímulo)

|

Share this awesome video!

|

Bienvenido al día de suerte número 7. Hoy hablamos de Stimulus: una biblioteca JavaScript pequeña y fácil de entender que nos permite crear código superorganizado que... simplemente siempre funciona. Es una de mis razones favoritas para utilizar Internet.

Instalar StimulusBundle

Pero aunque Stimulus es una librería JavaScript... Symfony tiene un bundle para ayudarnos a cargarla, configurarla y utilizarla. Así que, busca tu terminal y ejecuta:

composer require symfony/stimulus-bundle

Una de las cosas más importantes de StimulusBundle es su receta. Cuando termine, ejecuta:

git status

La Receta Cambia

Oooh. Ha hecho varios cambios. El primero está aquí, en assets/app.js. Encima -quitaré ese comentario- ahora importamos un nuevo bootstrap.js:

16 lines | assets/app.js
import './bootstrap.js';
// ... lines 2 - 16

Ese archivo inicia la aplicación Stimulus.

Observa que importa un módulo @symfony/stimulus-bundle:

6 lines | assets/bootstrap.js
import { startStimulusApp } from '@symfony/stimulus-bundle';
// ... lines 2 - 6

El símbolo @ no es importante: es sólo un carácter que utilizan los paquetes JavaScript con espacio de nombres. Lo importante es que se trata de una importación desnuda, lo que significa que el navegador intentará encontrar este paquete mirando nuestro mapa de importación.

Vale Abre importmap.php. La receta ha añadido dos nuevas entradas aquí:

31 lines | importmap.php
// ... lines 1 - 15
return [
// ... lines 17 - 23
'@hotwired/stimulus' => [
'version' => '3.2.2',
],
'@symfony/stimulus-bundle' => [
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
],
];

La primera es para el propio Stimulus, que ahora vive en el directorio assets/vendor/. La segunda es... una especie de paquete "falso" de terceros. Dice que @symfony/stimulus-bundledebe resolver a un archivo en nuestro directorio vendor/. Esto es un poco de fantasía: decimos import '@symfony/stimulus-bundle'... y eso importará en última instancia este archivo loader.js desde vendor/.

La receta también añadió un directorio controllers/ -el hogar de nuestros controladores de Stimulus personalizados- y un archivo controllers.json, del que hablaremos mañana.

Ah, y en base.html.twig, añadió esta línea ux_controller_link_tags():

49 lines | templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
// ... lines 4 - 7
{% block stylesheets %}
{{ ux_controller_link_tags() }}
{% endblock %}
// ... lines 11 - 14
</head>
// ... lines 16 - 47
</html>

¡Bórrala! Eso era necesario con AssetMapper 6.3, pero ya no. De todas formas, hablaremos de ello mañana.

Utilizar Stimulus

Vale: todo lo que hemos hecho es composer require este nuevo bundle. Y, sin embargo, cuando actualizamos la página y miramos la consola, ¡Stimulus ya está funcionando! Estosapplication #starting y application #start proceden de Stimulus. Es increíble.

Con StimulusBundle, cualquier cosa que pongamos en el directorio controllers/ estará automáticamente disponible como controlador de Stimulus. Así, el hecho de que tengamos unhello_controller.js significa que podemos utilizar un controlador llamado hello:

import { Controller } from '@hotwired/stimulus';
/*
* This is an example Stimulus controller!
*
* Any element with a data-controller="hello" attribute will cause
* this controller to be executed. The name "hello" comes from the filename:
* hello_controller.js -> "hello"
*
* Delete this file or adapt it for your use!
*/
export default class extends Controller {
connect() {
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
}
}

De hecho, podemos verlo ahora mismo. Cuando se activa este controlador, sustituye el texto del elemento al que está unido. Para comprobar que Stimulus funciona, inspecciona cualquier elemento de la página... e introduce un data-controller="hello".

Cuando pulse intro, ¡boom! Se activa el controlador.

Crear un controlador personalizado

Ha sido divertido, pero vamos a crear nuestro propio controlador real. Copia hello_controller.jsy crea un nuevo archivo llamado celebrate_controller.js. Eliminaré los comentarios y el método connect:

10 lines | assets/controllers/celebrate_controller.js
import { Controller } from '@hotwired/stimulus';
// ... lines 2 - 3
export default class extends Controller {
// ... lines 5 - 8
}

Éste es el objetivo: cuando pasemos el ratón por encima del logotipo, quiero llamar a un método del controlador que active la biblioteca js-confetti. Empieza por crear el método. Podría llamarse como quieras, pero poof() ¡seguro que es un nombre divertido!

Dirígete a app.js, copia el código de js-confetti y elimínalo:

16 lines | assets/app.js
// ... lines 1 - 9
import JSConfetti from 'js-confetti';
const jsConfetti = new JSConfetti();
jsConfetti.addConfetti();
// ... lines 14 - 16

Colócalo en el controlador celebrate... y mueve la declaración de importación a la parte superior:

import { Controller } from '@hotwired/stimulus';
import JSConfetti from 'js-confetti';
export default class extends Controller {
poof() {
const jsConfetti = new JSConfetti();
jsConfetti.addConfetti();
}
}

¡Genial! El último paso es activar esto en un elemento. Hazlo en base.html.twig. Veamos... aquí está el logotipo. Añade data-controller="celebrate". Y para activar la acción al pasar el ratón, di data-action=""... y la sugerencia es casi correcta. El formato es, primero: el evento JavaScript que queremos escuchar. En lugar de click, queremos mouseover. Luego ->, el nombre de nuestro controlador, # y el nombre del método: poof:

52 lines | templates/base.html.twig
// ... line 1
<html>
// ... lines 3 - 14
<body class="bg-black text-white font-mono">
<div class="container mx-auto min-h-screen flex flex-col">
<header class="my-8 px-4">
<nav class="flex items-center justify-between mb-4">
<div class="flex items-center">
<a
href="{{ path('app_homepage') }}"
data-controller="celebrate"
data-action="mouseover->celebrate#poof"
>
<img src="{{ asset('images/logo.png') }}" width="50" alt="Space Inviters Logo" >
</a>
// ... lines 27 - 29
</div>
// ... lines 31 - 36
</nav>
</header>
// ... lines 39 - 48
</div>
</body>
</html>

¡Ya está! ¡Refresca y celébralo! Cada vez que mouseover, llama al método. Puedes verlo abundantemente en la consola.

Vaya, en cuanto añadimos un controlador al directorio controllers/, ya está cargado y listo para funcionar. Recuerda, todo sin compilar.

Carga perezosa de controladores

Pero a veces puedes tener un controlador que sólo se utiliza en determinadas páginas... por lo que no quieres obligar a tu usuario a descargarlo inmediatamente en cada página. Si te encuentras en esa situación, puedes hacer que tu controlador sea perezoso. Es lo mejor.

Para ello, añade este comentario especial sobre él: stimulusFetch: 'lazy':

11 lines | assets/controllers/celebrate_controller.js
// ... lines 1 - 3
/* stimulusFetch: 'lazy' */
export default class extends Controller {
// ... lines 6 - 9
}

Sí, es una locura. Pero en cuanto hagamos eso, en lugar de descargar este archivo al cargar la página, esperará hasta que vea un elemento en la página condata-controller"celebrate".

Observa: borra temporalmente el data-controller. Luego vuelve, actualiza, y en las herramientas de red, si busco celebrate, no hay nada. Si buscoconfetti -desde que nuestro controlador importa- js-confetti, tampoco está. No se han descargado.

Limpia tus herramientas de red. Luego ve al logo y hackea ese data-controller. Estamos imitando lo que ocurriría si cargáramos algo de HTML fresco vía AJAX y... ese HTML fresco incluye un elemento con data-controller"celebrate".

En cuanto eso aparezca en la página, vuelve a las herramientas de red. ¡Aparecieron dos elementos nuevos! Se dio cuenta del data-controller y descargó el controlador y js-confetti... ya que eso se importa desde el controlador. Y funciona de maravilla. Esto me encanta.

Mantén este controlador perezoso, pero vuelve a base.html.twig, vuelve a añadir data-controller.

¡Una de las grandes cosas de Stimulus es que lo utiliza gente de toda la Interwebs! Y hay muchos controladores Stimulus prefabricados por ahí para ayudarnos a resolver problemas. Uno de ellos se llama Symfony-UX. Mañana nos sumergiremos en uno de sus paquetes.