Hello World con WebComponents

Lo fácil de este post hubiese sido hacer lo que vais a ver con Polymer que tan de moda está últimamente. Pero lo mejor desde mi punto de vista como lector es que comprendas el funcionamiento de WebComponents puesto Polymer en si no es más que un conjunto de Polyfills sobre la especificación y resolución de binding, cosa que desde mi punto de vista le falta a la especificación. Que sentido tiene tener templates y no haber resuelto el Binding.

Hoy vamos a empezar al revés y es mostrando el resultado de nuestro componente en los tres principales exploradores.

Chrome Versión 36.

image

Firefox 31.

image

Internet Explorer 11.

image

Es evidente que a fecha de hoy solamente puedes hacer pruebas sobre Chrome y si realmente quieres probar en todos no te queda otra que utilizar Polymer donde puedes ver la tabla de compatibilidad con los diferentes browser Browser Compatibility o bien Brick con Xtag.

Bueno visto esta pequeña introducción vamos con el proyecto completo y la explicación de cada uno de los componentes.

image

  1. index.html Archivo de inicio de nuestro app.
  2. hell-world.html Archivo donde vamos a definir el html y el css junto con una referencia a hello-world.js y que realmente es nuestro componente
  3. hello-world.js. Archivo JavaScript con la lógica de nuestro componente.
  4. style.css Archivo css de nuestra aplicación.

1. html de nuestro componente(hello-world.html).

image

Fijate en primer lugar en el nuevo elemente template este no es más que un nuevo elemento del DOM para este propósito. Que guarda el contenido del html pero sin ejecutar absolutamente nada, es decir que si tu tienes en tu template una etiqueta img hasta que no lo utilices no va a descargar absolutamente nada. Se basa en document fragment que esta presente sino mal recuerdo desde IE8.

document.createDocumentFragment

En segundo lugar definimos el estilo que queremos para nuestro componente y fíjate en el selector host que es el estilo que tu quieres dar a tu componente y que cualquier otra persona que lo consuma lo puede modificar desde fuera. También puedes utilizar host(<selector>).

Otra cosa que es de destacar es el uso de content y observa el atributo select. Le estamos diciendo en este caso que cuando alguien en nuestro componente utilice tag de tipo p los traiga a esa posición. No tienes límite a la hora de poner contents y si obvias el atributo select, te traerá todos los elementos que agrupe tu componente. En el caso de tener contents con select y uno sin ellos en este ultimo traerá todos los elementos hijos no seleccionados en el resto. Te recomiendo leer las especificaciones de estos dos nuevos elementos HTMLContentElement y HTMLShadowElement.

Te invito a que juegues con estas instrucciones JavaScript desde la consola de tu explorador una vez montado todo el ejemplo.

var hello =document.querySelector(‘hello-world p’)
hello.getDestinationInsertionPoints()
var content = hello.getDestinationInsertionPoints()[0]
content.getDistributedNodes()

Realmente tienes que tener en cuenta getDestinationInsertionPoints que te va a informar de donde ha sido insertado el tag p y desde este puedes ver los nodos que se han seleccionado con getDistributedNodes.

¿Como detectar que tu explorador soporta templates?

Sencillo ejecuta la siguiente instrucción desde la consola y si devuelve true tu navegador será compatible con templates.

‘content’ in document.createElement(‘template’)

2. Lógica de nuestro componente(hello-world.js)

image

Vamos con el análisis de este código.

En primer lugar vamos a destacar el uso de document.currentScript este nos va a dar acceso al documento de nuestro componente a través de la propiedad ownerDocument.

Seguidamente seleccionamos con querySelector el elemento template.

Definimos una función privada donde cambiamos nuestro strong con el valor del atributo who.

Creamos nuestro componente y para ello tenemos que heredar de HTMLElement.

A nuestro elemento le agregamos las siguientes funciones.

createCallback: Se ejecuta cuando se crea el componente y en nuestro caso creamos un ShadowRoot e importamos dentro de el el contenido de la template.

attachedCallback: Se ejecuta una vez que el elemento ha sido insertado en el DOM.

attributeChangedCallback. Se ejecuta cuando un atributo es agregado,modificado o eliminado y en nuestro caso nos sirve para observar el atributo who de nuestro componente y llamar a nuestra función setWho que cambia el textContent del strong . Recibe tres parametros el nombre del atributo, el valor antiguo y el nuevo valor.

Existe una cuarta función que es detachedCallback y que en este ejemplo no estamos utilizando y se ejecutaría una vez que quitemos el elemento del DOM.

Si quieres probar su funcionamiento impleméntala y ejecuta esta línea desde la consola de tu explorador.

document.body.removeChild(document.querySelector(‘hello-world’))

Por ultimo registramos nuestro componente llamando a la función document.registerElement a la que le pasamos dos parámetros:

1. El nombre de nuestro componente.

2. Un objeto con la propiedad “prototype” que apunta a nuestro elemento. Este objeto también acepta la propiedad extend(string) para poder heredar de un elemento del DOM como un button o bien de un componente creado por nosotros.

Bueno pues a estás alturas ya tenemos nuestro componente creado y es hora de consumirlo en nuestra página index.html.

image

Ves que sencillo es 🙂 en nuestro html tenemos que hacer lo siguiente.

1. Importar nuestro elemento con la siguiente instrucción.

<link rel=”import” href=”hello-wold.html”>

Observa que el rel con valor a import es el que hace que nuestro componente se pueda utilizar.

Para saber si tu explorador lo soporta ejecuta la siguiente instrucción.

‘import’ in document.createElement(‘link’)

De esta nueva funcionalidad hay bastante que hablar respecto a seguridad pero la abordaré en otro post.

2. Por último simplemente lo que hacemos es agregar nuestro elemento al html.

<hello-world who="querido lector {{time}}" ng-repeat="time in times">
            <p>te saludo desde un web component</p>
</hello-world>

Quizá en este momento si has trabajado con Angular te darás cuenta rápidamente que estoy utilizando Angular y tu te puedes preguntar pero es necesario Angular para todo esto. La respuesta es no. Simplemente quería demostrar que la compatibilidad con Angular es total, así como con cualquier librería que utilices. Y no pienses solamente en acciones que se pueden hacer desde el cliente, esto funcionaría perfectamente desde una vista Razor(MVC) o Jade(Node) y haciendo un foreach dentro de esta.

Por último vamos a comentar el archivo css.

image

Unos selectores un tanto raros y sobre todo ese /deep/. Que estamos haciendo? Sencillo modificando el color del strong de nuestro componente como consumidores del componente. Observa que el css del componente selector host le dije que el background era rojo y el color blanco pero en ese css establecí el selector strong a color blue. Vamos que soy del Barça Guiño, pero una vez que lo he visto en el browser porque no declarar mi otra pasión futbolística que es ser “español” “español” “español”, por eso le cambio el color a yellow como consumidor XDDDD.

Referencias.

HTML Imports

Custom Elements

HTML’s New Template Tag

Shadow DOM 101

Shadow DOM 201

Shadow DOM 301

Custom Events

Data-binding Revolutions with Object.observe()

MutationObserver

WebComponents

Polymer

Brick con Xtag

Por último os dejo el código completo para que podías probar y recuerda que solo funciona en Chrome.

Código fuente de todo el post

Conclusiones.

Que si como desarrollador web no ves que es necesario su implementación en todos los exploradores, me estás demostrando que lo tuyo no es la reutilización sino más bien el copy/paste.

Que librerías como JQuery y otras parecidas poco sentido tienen y en este aspecto es cuando si veo a WebComponents como una verdadera revolución y sobre todo por la capacidad de encapsulación del css ,al igual que lo fue en su momento JQuery.

Por último para esas personas que las hay tanto en un bando como en el otro, vamos a los que yo llamo talibanes. A mi me da lo mismo que esto lo haya propuesto Google.

Que Polymer sea de Google, que Angular sea de Google me la …., en definitiva que ya que hubiesen muchos Google’s y que los otros vayan corriendo a implementar estás funcionalidades que falta nos hacen a todos y que no hacen otra cosa que desde mi punto de vista de mejorar la web.

Y por último aunque debería ser “off topic” por aquello de no marranear  “el comparte lo que sepas, sin ningún tipo de interés” . Me voy a centrar en esas personas que no ven más que conspiraciones contra Internet Explorer, o más bien no saben que las mayores conspiraciones las ha hecho este  durante más de 20 años. Por una vez me siento feliz de ver que sus números son poco preocupantes en cuanto a impacto y cada día me preocupa menos su presencia y que cualquiera de las instrucción que aquí he expuesto tardarán como poco un par de años en estar totalmente operativas. Si a esto le sumo que nadie es capaz de decir públicamente lo que hace falta, veo una comunidad detrás de todo esto como poco “PODRIDA”. Así que seguir abogando en contra de lo imposible, y de esta forma es fácil responder en twitter a las tonterías con un simple hastag #ignorante. Con eso soluciono el problemas de mantener conversaciones tipo #salvame y simplemente las convierto en serias diciendo #ignorante.