Introducción a StencilJS y demo de integración con Angular

En este tutorial vamos a hablar de StencilJS una tecnología que permite crear Web Components nativos cumpliendo 100% con el estándar actual, funcionando de forma autónoma o integrándose al 100% con otras tecnologías web como Vue, React, y como vamos a demostrar con Angular. Para mí StencilJS es la tecnología perfecta para crear verdaderas librerías de Web Components reutilizables universalmente.

Índice de contenidos


1. Entorno

Este tutorial está escrito usando el siguiente entorno:

  • Hardware: Slimbook Pro 13.3″ (Intel Core i7, 32GB RAM)
  • Sistema Operativo: LUbuntu 16.04
  • Visual Studio Code 1.16.1
  • StencilJS next
  • @angular/cli 1.4.2
  • @angular 4.4.1

2. Introducción

Si me has seguido mínimamente por Twitter y en las charlas que he dado sabrás de mi obsesión por separar claramente el trabajo de los desarrolladores del de los arquitectos/diseñadores/UX a la hora de implementar una aplicación web:

Y es que hasta ahora la mejor solución para conseguir esto con Angular era hacer una librería de componentes reutilizables con Polymer, el “problema” de esta aproximación es que estas dos tecnologías casan perfectamente entre ellas pero no así con otras librerías de Web Components como Vue y React.

Digo hasta ahora porque el equipo de Ionic, famosos por el ecosistema de desarrollo de aplicaciones híbridas, nos ofrece StencilJS. Dirás, !venga va otro framework que se quiere comer el pastel y otro que hay que aprender! Pues te sorprenderás cuando sepas que esta tecnología no es ningún framework, ni tan siquiera una librería, es un compilador que genera Web Components 100% compatibles con el estándar, 100% compatibles con cualquier tecnología de las antes mencionadas y que gestiona de forma automática los polyfills que según el navegador que estemos utilizando necesite. ¡Cómo te quedas! :-O

Esto quiere decir que ahora sí podemos tener una librería de web components nativos que vamos a poder reutilizar sea cual sea (o no sea ninguno) el framework/librería que estemos utilizando, sin preocuparte por el navegador donde se ejecuten, haciendo uso de TypeScript con una sintaxis muy parecida a Angular y mezcla de React que resulta muy intuitiva, como veremos en el ejemplo.

Por ponerle una pega actualmente no tiene un catálogo de componentes ya desarrollados como el que cuenta Polymer, aunque en poco tiempo seguro que esto dejará de ser un problema y el equipo de Ionic ya ha anunciado que la versión 4 de Ionic tendrá todos los componentes actuales implementados con StencilJS.


3. Vamos al lío

Para nuestro primer componente con StencilJS vamos a implementar el componente “tnt-hello” que va a recibir un nombre para mostrar por pantalla y cuando se pulse sobre él va a mostrar una alerta mostrando el mismo nombre.

Un buen punto de partida es la documentación oficial

Esta documentación nos ofrece un proyecto “starter” para tener ya todo configurado que podemos utilizar ejecutando:

A continuación entramos dentro de la carpeta generada:

Eliminamos el remote origin original, dado que no queremos subir nada a este repositorio:

Instalamos todas las dependencias necesarias:

Y directamente podemos ejecutar el proyecto de prueba con:

En la URL http://localhost:3333 podremos ver algo parecido a esto:

Ya tenemos corriendo el proyecto de ejemplo que viene en el “starter”, y además con “live-reloading”, es el momento de crear nuestro componente.

Para ello podemos abrir el proyecto con nuestro editor de textos favorito, en mi caso Visual Studio Code donde si buscas en extensiones por la palabra “StencilJS” ya te salen dos muy útiles: un color syntax y un conjunto de snippets que nos ahorran escribir código. Dado que estamos trabajando con TypeScript, aconsejo incluir la extensión TSLint y Auto Imports.

Se agradece que el proyecto “starter” se mantenga simple y no cuente con muchos ficheros, uno de los más importantes es por supuesto el package.json que mantiene las dependencias y los scripts necesarios para la gestión de la configuración del proyecto y otro menos común, stencil.config.js que contiene información relativa a la forma de distribuir los componentes.

Los componentes tenemos que situarlos dentro de la carpeta “src/components”, aquí vamos a crear una nueva carpeta con el nombre de nuestro componente (“tnt-hello”) y dentro vamos a crear dos ficheros: el primero tnt-hello.scss para darle “estilo” al componente, con el siguiente contenido:

y el segundo tnt-hello.tsx que contendrá la lógica y el contenido visual, con el siguiente contenido:

Como ves el componente se define con el decorador @Component con estas dos propiedades:

  • tag: exactamente igual al selector en el componente de Angular; define el nombre de la etiqueta.
  • styleUrl: donde indicamos la localización del fichero que alberga el estilo del componente, que puede ser scss o directamente css.

Además en el componente definimos la propiedad de entrada “name” con el decorador @Prop de tipo string y el evento de salida “select” con el decorador @Event de tipo EventEmitter.

Nota: Esto es muy parecido al @Input y @Output para hacer el data binding en Angular.

Quizá lo que más llame la atención es la forma de definir el contenido HTML del componente, esto es heredado de React y se basa en el Virtual DOM que le confiere mayor rendimiento. Es por ello que hay que definir un método llamado “render” que va devolver el HTML formateado entre paréntesis. Fíjate que no estamos devolviendo ni un string ni un template string es puro HTML.

En el contenido HTML estamos mostrando el texto “Hello” seguido del valor de la propiedad “name” entre etiquetas h1. Fíjate que la interpolación de la variable se hace con un solo {}, en contra del {{}} y que tenemos que hacer uso de this para acceder al valor de la propiedad.

Para manejar el evento click sobre el h1, lo definimos con la palabra “onClick” seguida de la llamada al manejador, en este caso el método onSelect que simplemente hace uso del EventEmitter “select” para a través del método “emit” lanzar fuera del componente el valor de la propiedad “name” sin preocuparse por quién o quiénes estén escuchando ese evento.

Es el momento de hacer uso de nuestro componente en el proyecto y probar su funcionamiento. Para ello editamos el fichero index.html y justo debajo de la etiqueta “my-name” colocamos nuestro componente pasándole valor en el atributo “name”, de esta forma:

Además para capturar el evento lanzado cuando pulsemos sobre el componente vamos a hacer uso del método addEventListener que nos proporciona JavaScript, de esta forma:

Este sería el resultado con los dos componentes cuando pinchamos en el nuestro:

Como ves esta parte funciona perfectamente y ya tenemos dos componentes listos para usar en producción 🙂

Ahora vamos a ver cómo de bien se integran nuestros componentes de StencilJS con mi framework favorito Angular. Para ello editamos el fichero stencil.config.js donde añadimos nuestro componente al array de “components” y eliminamos la referencia al router, quedando de este modo:

Con esta configuración estamos indicando que queremos generar un único bundle con los dos componentes, en caso de querer bundles independientes por cada uno de ellos, lo definiríamos del siguiente modo:

Para generar los bundles deseados de producción solo tenemos que ejecutar:

Esto nos va a crear la carpeta “build” dentro de “www” que va a contener todos los ficheros necesarios para la distribución en producción de nuestros componentes. Por tanto este contenido es el que tenemos que hacer llegar a los proyectos que quieran hacer uso de ellos. La forma correcta es publicando la librería en algún repositorio npm ya sea público a privado, para este caso vamos a copiar esta carpeta y la vamos a pegar dentro de la carpeta “src/assets” de cualquier proyecto con angular-cli que tengamos, si no tenemos ninguno podemos crearlo ejecutando:

Ahora editamos el fichero src/index.html del proyecto de Angular, añadimos la referencia al fichero app.js de la carpeta assets/build y hacemos uso del componente tnt-hello fuera del ámbito de Angular, de esta forma:

Para poder hacer uso del componente en Angular, al igual que ocurre cuando hacemos uso de componentes de Polymer, que Angular no entiende porque no tiene registrados, tenemos que editar el fichero app.module y añadir la propiedad “schemas” con el valor CUSTOM_ELEMENTS_SCHEMA:

Vamos a incluir el componente dentro del template del componente principal pasándole un texto y recibiendo el texto de vuelta a través del evento. Para ello vamos a editar primero el fichero app.component.ts, dentro del método ngOnInit establemos valor al atributo name y creamos el método manejador del evento select que saltará cuando pinchemos sobre el h1 del componente. El resultado sería este:

Ahora solo nos queda editar el fichero app.component.html para hacer uso del componente en el template quedando de esta forma:

Nota: Fíjate como hacemos uso del “data binding” de Angular, para las propiedades utilizamos el corchete para que se evalúe la variable entre dobles comillas y para los eventos hacemos uso de los paréntesis para declarar el manejador del evento “select” que simplemente mostrará la alerta. Es importante que como cualquier evento con Angular lo denotemos exactamente con $event sino la información no será transmitida.

Si ejecutas este ejemplo tendrás que ver que en pantalla se muestra el texto de las dos instancias del componente, una dentro del ámbito de Angular y la otra fuera y que solo al pinchar en la de dentro se muestra la alerta con el texto.


4. Conclusiones

Como has podido ver StencilJS cumple con lo prometido, facilidad en la creación de Web Components y al menos empíricamente hemos demostrado que con Angular se integra a la perfección; lo que nos permite poder dividir los proyectos y que los desarrolladores hagan aplicaciones con Angular sin preocuparse del estilo, simplemente añadiendo las etiquetas que los arquitectos, diseñadores y UX les han facilitado para hacerla vistosa, usable y funcional en un tiempo record, donde cada rol se dedica a lo suyo.

Recordad que esta técnica y otras muchas más las encontraréis en la guía práctica de Angular y también ofrecemos cursos in-house y online.

Cualquier duda o sugerencia en la zona de comentarios.

Saludos.