web components (3): imports

El tercer pilar de los web components son los imports de html

Franz Marc

archivado en: JavaScript / 30 diciembre, 2015 / taller:

Imports

En las dos entradas anteriores de esta serie dedicada a los web components vimos que su arquitectura se apoya en cuatro grandes pilares: los templates, los custom elements, los imports y el shadow dom. En esta abordaré los imports que, como se deduce del nombre, sirven para importar fragmentos de código html, css y js a una web.

Su sintaxis es muy parecida al link que usamos para enlazar hojas de estilo, solo que en el atributo rel se escribe import en lugar de stylesheet.

<head>

<meta charset="UTF-8">

<link rel="import" href="mi-componente.html">

<title>Web Components</title>

</head>

Como ocurre con el require_once() de php, los imports solo traen una vez un documento externo aunque se declaren dos veces. Es decir, esto solo cargaría una vez mi-componente.html:

<link rel="import" href="mi-componente.html">

<link rel="import" href="mi-componente.html">

Para inyectar el código importado debemos realizar un procedimiento parecido al que vimos para los templates, pero utilizando import para recuperar el documento «en bruto», del que podemos seleccionar lo que queramos, manipularlo si lo necesitamos e inyectarlo en el documento principal. Por ejemplo, si este fuera nuestro componente importado:

mi-componente.html

<p id="dinosaurio">Soy un brontosaurio</p>

Para incluir el brontosaurio en la página principal podríamos hacer algo así:

...

<link rel="import" href="mi-componente.html" >

</head>

<body>

<div id="jurasic-era"></div>

<script>

var docuImportado = document.querySelector('link[rel="import"]');

var contenidoImportado = docuImportado.import;

var dinosaurio = contenidoImportado.getElementById('dinosaurio');

var contenedor = document.getElementById("jurasic-era");

contenedor.appendChild(dinosaurio.cloneNode(true));

</script>

Al igual que sucede con ajax o herramientas parecidas, los archivos importados deben pertenecer al mismo origen para evitar problemas de CORS (o tenerlas habilitadas), y en local hay que trabajar con algún servidor levantado, ya sea un apache, un node o lo que sea.

La fábrica de duendes

Para ver la problemática del javaScript y las css de los documentos importados y, de paso, llevar a la práctica lo visto hasta el momento, vamos a crear ahora un componente para incluir duendes en una página web. El componente debe ser capaz de leer el atributo nombre y mostrarlo en pantalla.

La página principal en la que irán nuestros duendes puede ser algo así:

index.html

<!DOCTYPE html>

<html lang="es">

<head>

<meta charset="UTF-8"><

script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>

<title>Web Components</title>

</head>

<body>

<da-duende nombre="Bailey Potfiller"></da-duende>

<da-duende nombre="Little Forrekettle"></da-duende>

<script>

// De momento incluiremos en la propia página principal la lógica del componente

</script>

</body>

</html>

El custom element

Lo primero que debemos hacer es crear el custom element, que de momento puede ser algo así:

<script>

/* Creamos un objeto a partir del prototitpo de un elemento HTML */

var prototipoDuende = Object.create(HTMLElement.prototype);

/* Hacemos cosas cuando nace el objeto en el DOM */

prototipoDuende.createdCallback = function() {

/* this aquí hace referencia a la respectiva etiqueta da-duende */

/* Leemos el nombre */

var nombre = this.getAttribute("nombre");

/* Lo ponemos en el contenido del tag */

this.innerHTML = nombre;

/* Los duendes son verdes */

this.style.color = "#090";

this.style.fontWeight = "900";

}

/* Registramos el elemento */

document.registerElement("da-duende", {

prototype: prototipoDuende

});

</script>

Ale op! Ahora ya tenemos dos duendes en pantalla.

practica-duende-00

 

La plantilla

Sin embargo, no nos ha quedado un componente muy limpio, puesto que hemos definido los estilos con javaScript en lugar de hacerlo con css y además nuestros duendes aparecen uno detrás de otro en lugar de ocupar sendos párrafos, lo que sin duda es mucho más elegante. ¡Arreglemos este desaguisado usando una plantilla!

<template id="plantilla-duende">

<style>

p {

color: #090;

font-weight: bold;

}

</style>

<p>Soy una plantilla formidable</p>

</template>

<script>

var prototipoDuende = Object.create(HTMLElement.prototype);

/* Seleccionamos la plantilla y cacheamos su contenido en una variable */

var plantillaDuende = document.getElementById("plantilla-duende").content;

prototipoDuende.createdCallback = function() {

var nombre = this.getAttribute("nombre");

/* Importamos el contenido de la plantilla */

var cloneDuende = document.importNode(plantillaDuende, true);

/* Seleccionamos el párrafo de la plantilla y le insertamos el nombre */

cloneDuende.querySelector("p").textContent = nombre;

/* Insertamos en el este tag (this) el clone */

this.appendChild(cloneDuende);

}

document.registerElement("da-duende", {

prototype: prototipoDuende

});

</script>

¡Mucho mejor ahora!

practica-duende-01

Import

Bueno, poco a poco nuestro componente comienza a estar listo y no, no te preocupes ahora por entenderlo todo, ya veremos más adelante que todo esto se puede hacer de forma más sencilla con polymer. Pero seguimos teniendo un problema y es que el componente solo nos vale para esta página web, cuando lo suyo es que sea reutilizable en cualquier lado. La solución es sencilla: vamos a sacar todo el mondongo a un archivo aparte y luego lo volvemos a traer, empaquetado, con un import.

En teoría, debería bastar con coger el código anterior, desde que empezamos a definir <template> hasta que cerramos el </script>, llevarlo a un archivo específico, que podemos llamar da-duende.html, y luego importarlo desde la página que sea:

<!-- importamos el componente -->

<link rel="import" href="da-duende.html">

Sin embargo, al hacer esto, deja de funcionar esta línea:

var plantillaDuende = document.getElementById("plantilla-duende").content;

La razón es algo enrevesada y es porque document hace referencia a la página principal y no a la página que contiene el componente. Así, para referirnos a este otro, tenemos que utilizar esta otra fórmula:

document.currentScript.ownerDocument;

currentScript devuelve el elemento <script> donde se está ejecutando, por lo que consultándole cuál es su ownerDocument, el documento al que pertenece, ya sí podríamos utilizar el selector. Sin embargo, aún nos queda algo por hacer.

Como vimos en la primera entrada de esta serie, en la página principal tenemos que incluir el polyfill de webcomponents.org para que funcione todo correctamente en el Explorer y este polyfil tiene un currentScript tuneado que han llamado igual, pero con un guion bajo delante. Así, nuestra línea problemática debería quedar al final de esta manera:

var plantillaDuende = document._currentScript.ownerDocument.getElementById("plantilla-duende").content;

Nuestra fábrica de duendes casi está lista, pero aún falta un problema por solucionar. Si incluimos un troll en la página, por ejemplo debajo de los dos duendes anteriores...

<da-duende nombre="Bailey Potfiller"></da-duende>

<da-duende nombre="Little Forrekettle"></da-duende>

<p>Soy un troll</p>

...descubriremos que también el troll es verde, ¡cuando es bien sabido que son grises o, si acaso, azules!

practica-duende-02

Este desastre se debe a que las css del componente no están encapsuladas y se han arrastrado a la página principal. Don panic! podemos solucionarlo, pero para eso debemos adentrarnos por las sombras del DOM, una aventura que emprenderemos en la próxima entrada, que por hoy lo dejo aquí.

|| Tags: ,

valoración de los lectores sobre web components (3): imports

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • 5 sobre 5 (4 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Aportar un comentario


*