Aplicaciones fáciles con hyperHTML — 5

Ivan Torres
Easy apps with hyperHTML
7 min readNov 9, 2018

Custom elements

English Version

Parte 5 escrita por Ivan Torres y Paul Thompson.

Traducción por Lupita Hernandez e Ivan Torres.

  1. Introducción, wire/bind
  2. Eventos y componentes
  3. Más acerca de componentes y manejo de estado simple
  4. Tipos de wires y definiciones personalizadas (intents)
  5. Custom elements con hyper
  6. Personalizando mis custom elements
  7. Corriendo pruebas!
  8. Carga asíncrona, placeholder y un Typeahead con hyper
  9. Manejando rutas
  10. Usando librerías externas (3rd party)

Mi propio elemento

Si has desarrollado paginas web o aplicaciones por cualquier cantidad de tiempo, lo mas seguro es que has visto componentes personalizados o etiquetas HTML, especialmente si has trabajado con alguno de los nuevos eframeworks de javascript. Cualquiera que sea el nombre, estos componentes usualmente tienen una meta similar: encapsular funciones y lógica en partes de código reutilizables. Después de mucha discusión y desarrollo por parte de la comunidad, finalmente se desarrollaron estas ideas bajo el dosel de Web Components.

Web Components constiste en distintas tecnologías independientes. Puedes pensar en Web Components como en widgets de interfaz de usuario reusables que son creados usando tecnología Web abierta. Son parte del navegador, y por lo tanto no necesitan bibliotecas externas como jQuery o Dojo. Un Web Component puede ser usado sin escribir código, simplemente añadiendo una sentencia para importarlo en una página HTML. Web Components usa capacidades estándar, nuevas o aún en desarrollo, del navegador. — MDN

Para poder lograr esto, la especificación aborda las siguientes tecnologías:

  1. Custom elements: Un set the APIs de javascript que te deja definir la logica de los elementos. Puedes crear funciones como open() y despues puedes llamar esa funcion desde tu element document.querySelector(‘custom-dialog’).open().
  2. Shadow DOM: Te permite usar otros elementos HTML dentro de tu custom element, hasta otros custom elements!. Todo el código dentro de este sera renderizado y estilizado por separado. Esto puede ser un bug o un feature, depende de tu caso de uso.
  3. HTML templates: Dos nuevos elementos <template> y <slot> estas te dejan escribir HTML dentro de tu elemento, pero no son renderizados o mostrados hasta que los actives. <template> y <slot> también pueden ser re-usados después para otras cosas.

No todas estas tecnologías están lista para usarse en todos los navegadores, y ciertos aspectos son mas difíciles de crear un polyfill que otros, pero estamos acercándonos.

Por ahora nos vamos a concentrar en custom elements, sin embargo al final te mostraremos como usar shadow DOM. Veremos como hyperHTML puede crear plantillas altamente eficientes. Veremos como podemos hacer todo lo que <template> puede hacer con hyperHTML, excepto el uso nativo de <slot>.

<custom-element awesome=”true”>

Para empezar a hacer custom elements, necesitamos hacer 3 cosas:

  1. Un navegador que los soporte, o un polyfill. Recomendamos usar document-register-element. Este ha sido utilizado en producción durante un tiempo por proyectos como AMP. Alternativamente, Google también tiene uno que puede revisar.
  2. Una clase que extienda el objetoHTMLElement. Esta clase contendrá toda la lógica de nuestro elemento. También podemos extender de cualquier otro elemento HTML.
  3. Finalmente, tenemos que “definir” el elemento. Esto es como vamos a registrar nuestro elemento en el navegador, y darle su nombre.

En el numero 2, mencionamos que hay dos formas de extender la clase de tus elementos, ya sea extendiendo HTMLElement, o extendiendo un elemento predefinido como span. Cuando extendemos HTMLElement nuestro elemento sera “autónomo”. Pero, extender elementos pre-definidos es un poco controversial aun, y aunque la especificación describe la forma de hacerlo, no todos los navegadores lo soportan. Veremos como hacer ambos, pero para nuestro caso de uso del blog, solo extenderemos de HTMLElement.

Mucho parloteo, muestrame el codigo!!

(un pequeño gran detalle, en index.html, como stackblitz esta automagicamente transpilando nuestro código, necesitamos incluir el es5-adapter)

Vamos a disecar el código un poco:

Primero nombramos nuestra clase y extendemos HTMLElement. Este sera un elemento autónomo. Después, tenemos una función llamada connectedCallback(), esta sera llamada después de que el navegador haya agregado el elemento al documento. Vamos a platicar mas sobre el ciclo de vida de los elementos mas adelante. Adentro de connectedCallback() estamos solamente añadiendo texto a nuestro elemento.

En la linea 9 estamos definiendo nuestro elemento. No importa el nombre que escojas, es necesario añadir un guion “-”. En nuestro caso, para usar este elemento vamos a usar la etiqueta <custom-element></custom-element>. La función de define también recibe un segundo parámetro que es la clase que describe el elemento.

Internamente, el navegador mantiene un registro de todos los custom elements que definimos. Una cosa que podemos hacer con el CustomElementRegistry es esperar a que los elementos estén definidos. Podemos hacer muchas mas cosas con este, así que revisa la documentación del link anterior.

Y solo para nuestra diversion, vamos a ver como extender un elemento pre-definido:

Ciclo de vida (Life cycle)

Ademas de connectedCallback() hay otras funciones que podemos usar. Veamos que hacen:

  • connectedCallback: llamada cuando el elemento es conectado al documento DOM.
  • disconnectedCallback: llamada cuando el elemento es separado del DOM. Puedes reaccionar a esta acción aquí.
  • adoptedCallback: llamada cuando mueves el elemento a una pagina nueva.
  • attributeChangedCallback: llamada cuando algunos de los atributos que estas monitoreando cambia. Ninguno de los atributos de tu elemento sera monitoreado automáticamente. Para escoger que atributos serán monitoreados puedes agregar esta función:
static get observedAttributes() {return ['attr1', 'attr2']; }

Vamos a ver estas funciones en acción:

Muy bien! Estamos en el camino de tener un nuevo elemento poderoso. Pero que haremos para la plantilla del elemento? Con hyperHTML, podemos renderear cualquier cosa que queramos dentro del elemento y boom!!, custom elements con todo el poder de hyperHTML :D.

Hay que hacer algunos cambios. Primero claro, tenemos que cargar hyperHTML. Después vamos a bind al elemento actual this.html = bind(this);. También vamos a necesitar una función render() la cual sera encargada de renderear la plantilla. Finalmente, necesitamos actualizar nuestro attributeChangedCallback() para que cualquier cambio de atributo también actualizar las propiedades.

Introduciendo hyperHTML-element

Hay mucho mas que aprender acerca de custom elements, pero afortunadamente el maestro Andrea Giammarchi creo una utilidad que abstrae mucho del API de los custom elements llamada hyperHTML-element.

Esta es una clase que puedes extender en lugar de HTMLElement, y provee algunas nuevas funciones y propiedades.

Funciones

  • Ademas de los observedAttributes también tenemos ahora booleanAttributes. Aqui es donde pondremos los atributos que no requiren un valor. Por ejemplo, <custom-element required>. “required” existe o no así que es un gran candidato para ponerlo como atributo booleano.
  • Tanto observedAttributes comobooleanAttributes definen getters y setters por cada atributo observado. Esto significa que no necesitamos hacer this[name] = newValue adentro de attributeChangedCallback.
  • created. Esta nueva función sera llamada justo antes de connectedCallback o attributeChangedCallback
  • defaultState(). Cada vez que uses this.state, la función defaultState() regresa un objeto que se convierte en, acertaste!, el estado por defecto.
  • setState. Actualiza el estado (no necesitas definir defaultState para user setState). Este incluye un pequeño extra: va a llamar render() por ti, asi que tu elemento es actualizado cada vez que cambia el estado.
  • render. Esta función debe de regresar el HTML que quieres mostrar en tu elemento.
  • Manejadores de eventos (event handlers). En tu plantilla, ahora puedes pasar “this” a el evento que quieras manejar: <div onclick=${this}></div>. Si después definimos la función onclick dentro de la clase del elemento, va a ser llamada con el contexto apropiado. También puedes usar data-call para llamar una función diferente: data-call=onAnyEvent onclick=${this}… en este caso la función onAnyEvent sera llamada cuando el usuario haga click en el elemento. Aun mas interesante, los eventos personalizados son muy sencillos de usar: onMyCustomEvent=${this} y defines la función onMyCustomEvent en la clase de tu elemento, listo!.
  • define(). Este método estático es para hacer mas sencilla la definición de elementos. En lugar de usar customElements.define usaras MyElement.define('my-element')
  • Todas las funciones de hyperHTML como bind, component y wire. La única función que no tenemos es define para los intents, dado que la estamos usando para registrar el elemento. Para definir intents vas a usar HyperHTMLElement.intent(…). Vimos estos en la parte 4 del blog, asegúrate de repasar esta parte si necesitas.

Los lectores observadores notarán que algunas de estas funciones se utilizan en hiper.Component. Espero que ya estés familiarizado con ellas :D.

Propiedades

  • this.html. En nuestro ejemplo anterior usamos this.html = bind(this); en el constructor. Con hyperHTML-element podemos usar this.html directamente.
  • state. State es un objecto que contiene el estado del elemento. Esta vació por defecto, o contendrá lo que sea que estas regresando en defaultState, si es que defines esa función.
  • Y como lo mencionamos anteriormente, todos los atributos definidos en observedAttributes y booleanAttributes van a tener sus getters y setters, así que al usar this.attr el valor que le des sera reflejado en el atributo. Por ejemplo, <my-element parent="mama"/>, si haces this.parent te dará el valor de “mama”.

Muy bien! Vamos a actualizar nuestro elemento previo para usar estas nuevas características:

Pero, y donde esta shadow DOM?… ah si… es muy fácil:

Revisa la linea 8 this.attachShadow({mode: ‘open’});, si inspeccionas tu elemento con el navegador veras que el shadow DOM esta ahi!.

Hay mucho mas que aprender sobre custom elements y web components en general. En la siguiente parte vamos a escribir algo de lógica en nuestro elemento y actualizar nuestra tabla de la parte 4 para que sea un custom element. Como siempre, tus comentarios son muy bien recibidos!.

--

--

Ivan Torres
Easy apps with hyperHTML

Multi-purpose Geek, Mobile/Web Developer, Father, mrpix, #nodejs