Webcomponents: Qué son y su uso con Polymer

Webcomponents: Qué son y su uso con Polymer

Ha pasado bastante tiempo desde la última vez que pude escribir en el blog, así que para compensar un poquito este tiempo de silencio, voy a explicar los conceptos básicos para empezar a trabajar con los webcomponents utilizando polymer para ello.

Como siempre, antes de empezar, aquí dejo la versión en video:

Webcomponents: ¿Esto qué es?

Los webcomponents vienen de la mano de HTML5 y son el futuro de las aplicaciones web. Los webcomponents nos permiten crear nuestras propias etiquetas HTML (bueno más bien componentes), determinando cual va a ser su comportamiento, tanto desde el punto de vista de la maquetación como desde la lógica de programación.

Básicamente, un webcomponent se compone de un fichero HTML con un marcado especial que funciona a modo de plantilla, de manera que con importar en tu HTML la plantilla o plantillas correspndientes ya puedes utilizar tu propias etiquetas.

Pensando en un modo algo más formal, podemos decir que los webcomponents son un compendio de “tecnologías” que se usan de forma conjunta, aunque pueden ser utilizadas de forma independiente:

  • Templates: Es la base de los webcomponents puesto que es dónde definimos su comportamiento y lo registramos como tal
  • Decorators: Es el CSS a utilizar en los webcomponents y estará ubicado en el template.
  • Custom Elements: Es lo que nos permite crear los nuevos componentes a utilizar
  • Shadow DOM: Básicamente es una estructura del DOM que se encuentra encapsulada dentro de una etiqueta HTML, de forma que como “usuarios” vemos la etiqueta HTML, pero en su interior hay un DOM oculto que realmente es el contenido que se visualiza. Un ejemplo de Shadow DOM sería lo que realmente contiene la etiqueta video de HTML5
  • Imports: Permite importar a nuestros documentos HTML nuestras plantillas.

¿Todo eso utilizamos cuando creamos un webcomponent? Pues sí… ahí es na!

Vale, pero no acabo de pillar qué son los webcomponents

En resumidas cuentas, con webcomponents creamos etiquetas HTML asociadas a una plantilla reutilizable. El típico ejemplo es imaginar escribir:

<google-map lon="" lat="" map="" zoom=""></google-map>

y que mágicamente esa etiqueta se transformase en el mapa de google centrado en el punto, modo de mapa y zoom indicados. Ahora se entiende mejor verdad? O imagina tener una etiqueta:

<user-register></user-register>

Que se transforme en formulario completo de registro!

Por supuesto, al igual que ocurre con las clases en cualquier lenguaje de programación, la comunidad comparte sus propios webcomponents para que todos podamos utilizarlos. Existen páginas dónde podemos descargarlos, por ejemplo:

Pues vamos a ver cómo se consigue esto, pero en vez del mapita de google, tiramos de otro clásico: un v-card.

Polymer y cómo crear webcomponents fácil y rápido

Los webcomponents como son parte de HTML5, sólo los navegadores modernos los soportan. Sin embargo, la comunidad hace cosas realmente maravillosas y existe webcomponents.js, una serie de librerías JS para dar soporte a los navegadores antiguos, entre otras cosas. Ahora bien, crear un webcomponent no siempre es tan sencillo como pueda parecer… afortunadamente tenemos el proyecto Polymer Project que nos salva la vida… y de qué forma.

Por lo tanto, debemos tener claro que para crear webcomponents vamos a utilizar Polymer. Existen tres formas de instalar Polymer, tal y como informan desde la web del proyecto: https://www.polymer-project.org/1.0/docs/start/getting-the-code.html

  • Utilizando bower.
  • Descargándolo en formato ZIP
  • Obteniéndolo desde github

Desde Polymer recomiendan utilizar bower puesto que se trata de un gestor de paquetes para web con el que no sólo instalar Polymer y sus dependencias sino que puedes instalar todo aquello que quieras utilizar del lado del cliente.

Para este artículo, en lugar de bower utilizaremos la descarga en formato ZIP, ya habrá tiempo de hablar de bower.

La descarga la obtenemos en esta URL: http://zipper.bowerarchiver.appspot.com/archive?polymer=Polymer/polymer%231.0.0

Lo que vamos a descargar es tanto la versión 1.0.0 de Polymer como webcomponents.js de forma que tenemos todo lo necesario. Tras descargarlo lo descomprimimos y creamos nuestra estructura de aplicación, de manera que tengamos algo tal que así:

Ejemplo de estructura de proyecto con webcomponents

Ejemplo de estructura de proyecto con webcomponents

Como se puede ver, el directorio bower_components es el resultado de descomprimir la descarga del polymer 1.0.0… además tenemos un directorio custom; la idea de este directorio es que almacene nuestros webcomponents, para tener todo bien ordenadito y controlado.

Ya toca empezar a trabajar!

Decidir el nombre de nuestros webcomponents

Lo primero que tenemos que decidir es cómo se van a llamar los webcomponents que creemos. Como en nuestro caso queremos una etiqueta HTML que sirva para definir y presentar la típica tarjetita de autor, la llamaremos v-card. OJO, el guión medio es importante, si utilizmos vcard, fallará.

Además del nombre de la etiqueta, necesitamos decidir qué atributos va a tener la etiqueta puesto que los atributos son los parámetros de entrada que va a recibir nuestro webcomponent, como si de parámetros de una función se tratasen.

Así pues, nosotros vamos a poner los siguientes:

  • image: La imagen del autor
  • firstname: El nombre de la persona
  • surname: Los apellidos
  • site: La URL de su página web
  • email: Su dirección de email
  • twitter: Su nombre de usuario en twitter
  • linkedin: La parte variable de la URL de linkedin

O sea, que nuestra etiqueta HTML o webcomponent se podría utilizar tal que así:

<v-card image="loquesea.png" firstname="Rolando" surname="Caldas Sánchez" site="https://rolandocaldas.com/" email="correo@example.com" twitter="rolando_caldas" linkedin="rolandocaldas"></v-card>

Bien, ya sabemos a dónde queremos llegar, ahora toca ir paso a paso.

Creando la plantilla de nuestro webcomponent

Como queremos crear un webcomponent bajo la etiqueta v-card, dentro del directorio custom creamos un fichero que llamaremos v-card.html de manera que éste fichero será dónde tendremos la lógica y la presentación de nuestro webcomponent.

Este fichero es un documento HTML, pero tiene alguna peculiaridad. Lo primero que tenemos que tener claro es que estamos creando un webcomponent y que vamos a utilizar Polymer para ello, por lo que es lógico que la primera línea de nuestro v-card.html tenga por finalidad llamar a Polymer:

<link rel="import"
      href="../bower_components/polymer/polymer.html">

Para cargar polymer, hemos utilizado un import, uno de los elementos utilizados en la generación de webcomponents. Puede que llegado este punto te preguntes por qué en cada webcomponent tenemos que andar a importar polymer, ¿no sería más sencillo que en nuestro documento HTML (el index.html) llamásemos a polymer en lugar de hacerlo en cada template de webcomponents? Bueno… para nosotros como desarrolladores igual es más sencillo, pero la finalidad de un webcomponent es proporcionar un componente encapsulado autosuficiente, que se pueda utilizar importando sólo su plantilla y dejando que sea el propio webcomponent quien cargue sus dependencias (con ciertas licencias claro). Así que, es mucho más correcto hacerlo como está establecido.

Bueno, con Polymer cargado, lo siguiente es indicar dónde empieza y dónde termina nuestro webcomponent. Estamos hablando de HTML, así que para ello utilizamos una etiqueta HTML de apertura y cierre:

<link rel="import"
      href="../bower_components/polymer/polymer.html">
<dom-module id="v-card">
</dom-module>

Si ya has leído algo sobre Polymer, igual te resulta extraño que utilice dom-module en lugar de polymer-element, esto es porque dom-module es para polymer 1.0.0 y polymer-element se utilizaba en la versión 0.5, en la 1.0.0 no existe y te daría error.

La etiqueta dom-module lleva un id y el valor de ese id es el nombre de nuestro componente, que coincidirá con el nombre de nuestro fichero y con la etiqueta HTML que utilizaremos; en nuestro caso: v-card.

En el interior de esta etiqueta HTML colocaremos el código del template, de los decorators, el shadow dom y el JS necesario para registrar y utilizar nuestro webcomponent desde Polymer.

Lo primero que vamos a hacer es registrar nuestro webcomponent con Polymer, así que toca escribir un poco de JavaScript:

<link rel="import"
      href="../bower_components/polymer/polymer.html">
<dom-module id="v-card">
    <script>
        Polymer({
            is: "v-card"
        });
    </script>
</dom-module>

Lo único que hemos hecho es registrar v-card, nada más. Por ahora nos sirve, aunque más adelante revisitaremos este código para aumentar la configuración. Digo configuración porque, lo que estamos haciendo es lanzar Polymer enviando unos parámetros de configuración a modo de objeto que serán utilizados para crear la etiqueta HTML en el DOM tal y como la definamos. Por ahora, lo dicho, sólo le enviamos el nombre de la etiqueta: v-card.

Bueno, en nuestra plantilla ya hemos cargado polymer, definido el área dónde se especificará nuestro webcomponent y lo hemos registrado como tal vía Polymer. Ahora vamos a escribir el contenido de nuestra etiqueta… o nuestro shadow-dom:

<link rel="import"
      href="../bower_components/polymer/polymer.html">
<dom-module id="v-card">
    <template>
    </template>
    <script>
        Polymer({
            is: "v-card"
        });
    </script>
</dom-module>

Una etiqueta nueva, yeah! La etiqueta template la utilizamos para informar que en su interior se encontrará el código HTML que el navegador debe “visualizar” cuando nosotros escribamos la etiqueta v-card… vamos, la parte HTML del shadow-dom. Por ahora vamos a poner un simple “Hola mundo!”:

<link rel="import"
      href="../bower_components/polymer/polymer.html">
<dom-module id="v-card">
    <template>
        Hola mundo!
    </template>
    <script>
        Polymer({
            is: "v-card"
        });
    </script>
</dom-module>

Guardamos nuestro v-card.html y ya tenemos nuestro primer webcomponents terminado. Aunque parecía mucho lío al final no es tanto ¿verdad? Venga, pues vamos a utilizarlo!

Incorporar los webcomponents a nuestra página

Para utilizar nuestros webcomponents en cualquier página web, sólo debemos importarlo como tal. La importación se hace igual que cuando incluimos polymer en nuestro webcomponent, con la salvedad de que en el caso de una página web propiamente dicha, los imports van en la cabecera del documento, por lo que una página HTML5 que sólo utilice nuestro v-card tendría por código lo siguiente:

<!DOCTYPE html>
<html>
    <head>
        <title>V-CARD WebComponent example</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script type="text/javascript" src="components/bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
        <link rel="import" href="components/custom/v-card.html">
    </head>
    <body>
        <v-card 
            image="ar.png" 
            firstname="Rolando" 
            surname="Caldas Sánchez" 
            site="https://rolandocaldas.com/" 
            email="correo@example.com" 
            twitter="rolando_caldas" 
            linkedin="rolandocaldas">No webcomponent support</v-card>
    </body>
</html>

Vemos cómo en el head hacemos el import nuestro v-card, que luego utilizamos en el body. Además de ello, en el head cargamos el webcomponents-lite.min.js, es algo que debemos hacer para garantizar el correcto funcionamiento de los webcomponents… recordemos que es HTML5 y como pasa con tantas partes de HTML5 no existe todavía una implementación nativa al 100% por parte de los navegadores, algo que se encarga de “solucionar” webcomponents.js, por supuesto, se supone que en algún momento del futuro (o en un mundo paralelo) podremos eliminar este fichero JS sin problema.

El resultado de ver esta página en nuestro navegador web es…. tachaaaannnn:

Hola mundo con webcomponents

Hola mundo con webcomponents

Y lo que nuestro navegador interpreta:

Lo que carga el navegador con nuestro webcomponents

Lo que carga el navegador con nuestro webcomponents

Pues sí, incorpora el contenido de nuestro template 😉 Listo, nuestro webcomponent creado y funcionando.

Personalizar la apariencia de nuestros webcomponents con CSS

Aunque seguramente lleves ya un rato leyendo este artículo, realmente el resultado final está bastante lejos de lo que realmente tiene que hacer nuestro v-card. Vamos a empezar a solucionar el problema.

Para ello, lo primero que vamos a hacer es modificar el shadow dom de nuestro webcomponent para que su etiquetado sea más cercano a lo que queremos:

<link rel="import"
      href="../bower_components/polymer/polymer.html">
<dom-module id="v-card">
    <template>
        <div class="box">
            <div class="image">
                <img src="https://1.gravatar.com/avatar/45dc8f01ab6c5b503d2e4f21569935e1" alt="Rolando Caldas Sánchez" title="Rolando Caldas Sánchez" />
            </div>
            <div class="data">
                Rolando Caldas Sánchez
                <a href="mailto:correo@example.com" class="fa fa-envelope-o">correo@example.com</a>
                <a href="https://rolandocaldas.com/" class="fa fa-globe">https://rolandocaldas.com/</a>
                <a href="https://twitter.com/rolando_caldas" class="fa fa-twitter">@rolando_caldas</a>
                <a href="https://es.linkedin.com/in/rolandocaldas" class="fa fa-linkedin">rolandocaldas</a>
            </div>
        </div>
    </template>
    <script>
        Polymer({
            is: "v-card"
        });
    </script>
</dom-module>

Si comprobamos cómo se ve:

Así se ve nuestro webcomponent

Así se ve nuestro webcomponent

Tenemos ya el etiquetado correcto dentro de nuestro v-card. Sin embargo, lo vemos sin estilo alguno, así que toca empezar con el CSS.

Para agregar CSS a nuestro webcomponent sólo necesitamos ponerlo en la correspondiente plantilla, dentro del dom-module. Aunque podemos escribir nuestro CSS en cualquier sitio dentro de la etiqueta dom-module (siempre que sea un hijo directo), lo declararemos como la primera etiqueta dentro de dom-module, para mantener un orden:

<link rel="import"
      href="../bower_components/polymer/polymer.html">
<dom-module id="v-card">
    <style>
        :host {
            background: rgba(0, 0, 255, 0.1);
            box-shadow: 2px 2px 5px 2px rgba(0, 0, 0, 0.3);
            padding: 1rem;
            display: block;
            width: 50%;
            max-width: 40rem;
        }

        .box:after,
        .box:before {
            content: "";
            display: table;
        }
        
        .box:after {
            clear: both;
        }
        
        .box .image {
            float: left;
        }
        
        .box .data {
            float: left;
            padding-left: 1rem;
        }
        
        .box .data .name {
            margin-bottom: 1rem;
        }
        
        .box .data .contact {
            margin-bottom: 0.5rem;
        }
    </style>
    <template>
        <div class="box">
            <div class="image">
                <img src="https://1.gravatar.com/avatar/45dc8f01ab6c5b503d2e4f21569935e1" alt="Rolando Caldas Sánchez" title="Rolando Caldas Sánchez" />
            </div>
            <div class="data">
                <div class="name">Rolando Caldas Sánchez</div>
                <div class="contact">
                    <a href="mailto:correo@example.com" class="fa fa-envelope-o">correo@example.com</a>
                    <a href="https://rolandocaldas.com/" class="fa fa-globe">https://rolandocaldas.com/</a>
                </div>
                <div class="network">
                    <a href="https://twitter.com/rolando_caldas" class="fa fa-twitter">@rolando_caldas</a>
                    <a href="https://es.linkedin.com/in/rolandocaldas" class="fa fa-linkedin">rolandocaldas</a>
                </div>
            </div>
        </div>
    </template>
    <script>
        Polymer({
            is: "v-card"
        });
    </script>
</dom-module>

Como se puede comprobar, se trata de código CSS normal, nada del otro mundo. Lo único que llama a atención es el uso de :host

Todas las declaraciones CSS que estén dentro de la regla aplicable a :host se transformará en el CSS aplicable a la etiqueta de nuestros webcomponents, en nuestro caso será el CSS que el navegador aplicará a la etiqueta v-card. Además, todas las reglas CSS que definimos en la plantilla, serán aplicadas sólo a las etiquetas HTML hijas del propio v-card:

Nuestro webcomponent v-card con estilo!

Nuestro webcomponent v-card con estilo!

Y con algo tan sencillo, ya tenemos CSS para nuestro v-card. Eso sí, hasta ahora nuestro v-card tiene truco, porque claro… no estamos utilizando los atributos del webcomponent para nada y el contenido que se visualiza es estático.

Utilizando los atributos de nuestro webcomponents en el shadow-dom

Al principio de todo definimos una serie de atributos que tiene nuestra etiqueta v-card; gracias a Polymer utilizarlos en nuestro webcomponent es extremadamente, sencillo. Para poder utilizar los atributos necesitamos realizar dos pasos:

  1. Informar a Polymer de los atributos a utilizar y sus características
  2. Utilizar los atributos en el template de nuestro webcomponent

Informar a Polymer de los atributos a utilizar y sus características

Cuando en nuestra plantilla lanzamos la función de JS “Polymer”, hasta hora le mandábamos un objeto de configuración con la propiedad is, que nos permitía registrar la etiqueta como parte del DOM. Además de esa propiedad “is”, el objeto de configuración puede tener una propiedad llamada properties dónde se indican los atributos de nuestro v-card que utilizaremos:

        Polymer({
            is: "v-card",
            properties: {
                email: {
                    type: String
                },
                firstname: {
                    type: String
                },
                image : {
                    type: String,
                    value : "anon.png"
                },
                linkedin: {
                    type: String
                },
                site: {
                    type: String
                },
                surname: {
                    type: String
                },
                twitter: {
                    type: String
                }
            }
        );

Cada propiedad dentro de properties es un atributo de v-card. Vemos cómo utilizamos “type” para indicar el tipo de dato que contiene el atributo. Además, en el momento de agregar el atributo “image”, también indicamos ‘value: “anon.png”‘; lo que hacemos con ésto es agregar un valor por defecto para image, valor que será el asignado a esa propiedad en el caso de que al escribir nuestra etiqueta HTML v-card no incluyamos el atributo image.

Utilizar los atributos en el template de nuestro webcomponent

El segundo paso es utilizar dentro de nuestro template los atributos definidos en el objeto properties de la configuración de nuestro webcomponent facilitada a Polymer. Para ello, sólo debemos escribir {{nombreAtributo}} súper sencillo eh!

        <div class="box">
            <div class="image">
                <img src="{{image}}" alt="{{firstname}} {{surname}}" title="{{firstname}} {{surname}}" />
            </div>
            <div class="data">
                <div class="name">{{firstname}} {{surname}}</div>
                <div class="contact">
                    <a href="mailto:{{email}}" class="fa fa-envelope-o">{{email}}</a>
                    <a href="{{site}}" class="fa fa-globe">{{site}}</a>
                </div>
                <div class="network">
                    <a href="https://twitter.com/{{twitter}}" class="fa fa-twitter">@{{twitter}}</a>
                    <a href="https://es.linkedin.com/in/{{linkedin}}" class="fa fa-linkedin">{{linkedin}}</a>
                </div>
            </div>
        </div>

Comprobando el resultado

Aplicando lo indicado a nuestra plantilla, tendríamos el siguiente código en nuestro v-card.html

<link rel="import"
      href="../bower_components/polymer/polymer.html">
<dom-module id="v-card">
    <style>
        :host {
            background: rgba(0, 0, 255, 0.1);
            box-shadow: 2px 2px 5px 2px rgba(0, 0, 0, 0.3);
            padding: 1rem;
            display: block;
            width: 50%;
            max-width: 40rem;
        }

        .box:after,
        .box:before {
            content: "";
            display: table;
        }
        
        .box:after {
            clear: both;
        }
        
        .box .image {
            float: left;
        }
        
        .box .data {
            float: left;
            padding-left: 1rem;
        }
        
        .box .data .name {
            margin-bottom: 1rem;
        }
        
        .box .data .contact {
            margin-bottom: 0.5rem;
        }

    </style>    
    <template>
        <div class="box">
            <div class="image">
                <img src="{{image}}" alt="{{firstname}} {{surname}}" title="{{firstname}} {{surname}}" />
            </div>
            <div class="data">
                <div class="name">{{firstname}} {{surname}}</div>
                <div class="contact">
                    <a href="mailto:{{email}}" class="fa fa-envelope-o">{{email}}</a>
                    <a href="{{site}}" class="fa fa-globe">{{site}}</a>
                </div>
                <div class="network">
                    <a href="https://twitter.com/{{twitter}}" class="fa fa-twitter">@{{twitter}}</a>
                    <a href="https://es.linkedin.com/in/{{linkedin}}" class="fa fa-linkedin">{{linkedin}}</a>
                </div>
            </div>
        </div>
    </template>
    <script>
        Polymer({
            is: "v-card",
            properties: {
                email: {
                    type: String
                },
                firstname: {
                    type: String
                },
                image : {
                    type: String,
                    value : "anon.png"
                },
                linkedin: {
                    type: String
                },
                site: {
                    type: String
                },
                surname: {
                    type: String
                },
                twitter: {
                    type: String
                }
            }
        });
    </script>
</dom-module>

Y nuestro navegador nos muestra….

WTF!!!

WTF!!!

Ale, la primera en la frente eh? Lo que ocurre no es más que una “limitación” reconocida de Polymer. Cuando utilizamos los {{loquesea}} debemos hacerlo dentro de una etiqueta HTML y no puede haber nada más. O sea, lo siguiente fallará:

{{uno}} {{dos}}
{{uno}} dos

Y lo siguiente no:

<span>{{uno}}</span><span>{{dos}}</span>
<span>{{uno}}</span> dos

Así que debemos realizar cambios en nuestro template:

    <template>
        <div class="box">
            <div class="image">
                <img src="{{image}}" alt="{{firstname}} {{surname}}" title="{{firstname}} {{surname}}" />
            </div>
            <div class="data">
                <div class="name"><span>{{firstname}}</span> <span>{{surname}}</span></div>
                <div class="contact">
                    <a href="mailto:{{email}}" class="fa fa-envelope-o">{{email}}</a>
                    <a href="{{site}}" class="fa fa-globe">{{site}}</a>
                </div>
                <div class="network">
                    <a href="https://twitter.com/{{twitter}}" class="fa fa-twitter">@<span>{{twitter}}</span></a>
                    <a href="https://es.linkedin.com/in/{{linkedin}}" class="fa fa-linkedin">{{linkedin}}</a>
                </div>
            </div>
        </div>
    </template>

Y comprobamos de nuevo el resultado:

Ahora parece que todo va mejor...

Ahora parece que todo va mejor…

Parece que todo mejor no? Pues… sí y no. Veamos lo que interpreta el navegador y fijémonos en el contenido del atributo href de los enlaces:

Ains... estos enlaces malos...

Ains… estos enlaces malos…

pfff claro… no se substituye… pero no podemos meter un span dentro de un href…. así que ¿cómo hacemos? Pues Polymer ha pensado en todo!! Cuando llamamos a Polymer, el objeto de configuración también permite agregar propiedades que no forman parte de los atributos de nuestra etiqueta y cuyo valor se compone en un función.

Mejor un ejemplo, para crear una propiedad fullname que podamos usar en la plantilla agregamos un elmento nuevo a propierties:

        Polymer({
            is: "v-card",
            properties: {
                email: {
                    type: String
                },
                firstname: {
                    type: String
                },
                image : {
                    type: String,
                    value : "anon.png"
                },
                linkedin: {
                    type: String
                },
                site: {
                    type: String
                },
                surname: {
                    type: String
                },
                twitter: {
                    type: String
                },
                fullname : {
                    type: String,
                    computed: 'computeFullName(firstname, surname)'
                }
            }
        });

Este nuevo elemento fullname, además de definirlo como String, tenemos una propiedad nueva “computed” cuyo valor es ‘computeFullName(firstname, surname)’. Esto significa que se definirá la propiedad fullname y su valor será lo que retorne el método computeFullName que definiremos al mismo nivel que el obbjeto properties. Este computeFullName recibirá como parámetros la propiedad firstname y surname. Agregamos este nuevo método de forma que nuestro script queda:

        Polymer({
            is: "v-card",
            properties: {
                email: {
                    type: String
                },
                firstname: {
                    type: String
                },
                image : {
                    type: String,
                    value : "anon.png"
                },
                linkedin: {
                    type: String
                },
                site: {
                    type: String
                },
                surname: {
                    type: String
                },
                twitter: {
                    type: String
                },
                fullname : {
                    type: String,
                    computed: 'computeFullName(firstname, surname)'
                }
            },
            computeFullName : function (firstname, surname) {
                return firstname + ' ' + surname;
            }
        });

Aplicamos esta misma idea para la URL del correo, twitter y linkedin:

        Polymer({
            is: "v-card",
            properties: {
                email: {
                    type: String
                },
                firstname: {
                    type: String
                },
                image : {
                    type: String,
                    value : "anon.png"
                },
                linkedin: {
                    type: String
                },
                site: {
                    type: String
                },
                surname: {
                    type: String
                },
                twitter: {
                    type: String
                },
                emailURL : {
                    type: String,
                    computed: 'computeEmailURL(email)'
                },
                fullname : {
                    type: String,
                    computed: 'computeFullName(firstname, surname)'
                },
                linkedinURL : {
                    type: String,
                    computed: 'computeLinkedinURL(linkedin)'
                },
                twitterURL : {
                    type: String,
                    computed: 'computeTwitterURL(twitter)'
                }
            },
            computeEmailURL : function (email) {
                return 'mailto:' + email;
            },
            computeFullName : function (firstname, surname) {
                return firstname + ' ' + surname;
            },
            computeLinkedinURL : function (linkedin) {
                return 'https://es.linkedin.com/in/' + linkedin;
            },
            computeTwitterURL : function (twitter) {
                return 'https://twitter.com/' + twitter;
            }
        });

Trasladamos las nuevas propiedades a nuestro template:

    <template>
        <div class="box">
            <div class="image">
                <img src="{{image}}" alt="{{fullname}}" title="{{fullname}}" />
            </div>
            <div class="data">
                <div class="name"><span>{{firstname}}</span> <span>{{surname}}</span></div>
                <div class="contact">
                    <a href="mailto:{{email}}" class="fa fa-envelope-o">{{email}}</a>
                    <a href="{{site}}" class="fa fa-globe">{{site}}</a>
                </div>
                <div class="network">
                    <a href="{{twitterURL}}" class="fa fa-twitter">@<span>{{twitter}}</span></a>
                    <a href="{{linkedinURL}}" class="fa fa-linkedin">{{linkedin}}</a>
                </div>
            </div>
        </div>
    </template>

Y a probar en el navegador!

Ahora sí, todo correcto.

Ahora sí, todo correcto.

Templates condicionales dentro del template de nuestros webcomponents

Polymer tiene un sinfín de utilidades; para terminar este artículo aprenderemos a encerrar parte de nuestro template en otro template condicional.

Aunque nuestro webcomponents ya funciona bien, tal y como lo tenemos, salvo el atributo image de nuestro v-card, todos son obligatorios, si nuestro index.html fuese el siguiente:

<!DOCTYPE html>
<html>
    <head>
        <title>V-CARD WebComponent example</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script type="text/javascript" src="components/bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
        <link rel="import" href="components/custom/v-card.html">
    </head>
    <body>
        <v-card 
            image="https://1.gravatar.com/avatar/45dc8f01ab6c5b503d2e4f21569935e1" 
            firstname="Rolando" 
            surname="Caldas Sánchez" >No webcomponent support</v-card>
    </body>
</html>

En el navegador veríamos lo siguiente:

Algo no se ve del todo bien...

Algo no se ve del todo bien…

Puede parecer poca cosa, pero si vemos el código que interpreta:

<body>
        <v-card image="https://1.gravatar.com/avatar/45dc8f01ab6c5b503d2e4f21569935e1" firstname="Rolando" surname="Caldas Sánchez">
        <div class="box style-scope v-card">
            <div class="image style-scope v-card">
                <img class="style-scope v-card" src="https://1.gravatar.com/avatar/45dc8f01ab6c5b503d2e4f21569935e1" title="Rolando Caldas Sánchez" alt="Rolando Caldas Sánchez">
            </div>
            <div class="data style-scope v-card">
                <div class="name style-scope v-card"><span class="style-scope v-card">Rolando</span> <span class="style-scope v-card">Caldas Sánchez</span></div>
                <div class="contact style-scope v-card">
                    <a class="fa fa-envelope-o style-scope v-card"> </a>
                    <a class="fa fa-globe style-scope v-card"> </a>
                </div>
                <div class="network style-scope v-card">
                    <a class="fa fa-twitter style-scope v-card">@<span class="style-scope v-card"> </span></a>
                    <a class="fa fa-linkedin style-scope v-card"> </a>
                </div>
            </div>
        </div>
    </v-card>
</body>

Podemos ver cómo aunque no se especifica twitter, linkedin, web… ni siquiera el email, nuestra plantilla se ejecuta y quedan ahí los enlaces “vacíos”. Lo suyo es que si en el v-card no definimos linkedin, no salga el enlace a linkedin etc.

Para lograr esto, debemos encerrar la etiqueta del enlace dentro de una condición, de forma que ese parte del template “se cargue si”. Ahí es dónde entra en juego la ya conocida etiqueta template. Podemos encerrar el enlace tal que así:

<template is="dom-if" if="{{linkedin}}">
    <a href="{{linkedinURL}}" class="fa fa-linkedin">{{linkedin}}</a>
</template>

De esta forma, nuestro v-card.html queda tal que así:

<link rel="import"
      href="../bower_components/polymer/polymer.html">
<dom-module id="v-card">
    <style>
        :host {
            background: rgba(0, 0, 255, 0.1);
            box-shadow: 2px 2px 5px 2px rgba(0, 0, 0, 0.3);
            padding: 1rem;
            display: block;
            width: 50%;
            max-width: 40rem;
        }

        .box:after,
        .box:before {
            content: "";
            display: table;
        }
        
        .box:after {
            clear: both;
        }
        
        .box .image {
            float: left;
        }
        
        .box .data {
            float: left;
            padding-left: 1rem;
        }
        
        .box .data .name {
            margin-bottom: 1rem;
        }
        
        .box .data .contact {
            margin-bottom: 0.5rem;
        }

    </style>    
    <template>
        <div class="box">
            <div class="image">
                <img src="{{image}}" alt="{{fullname}}" title="{{fullname}}" />
            </div>
            <div class="data">
                <div class="name"><span>{{firstname}}</span> <span>{{surname}}</span></div>
                <div class="contact">
                    <template is="dom-if" if="{{email}}">
                        <a href="{{emailURL}}" class="fa fa-envelope-o">{{email}}</a>
                    </template>
                    <template is="dom-if" if="{{site}}">
                        <a href="{{site}}" class="fa fa-globe">{{site}}</a>
                    </template>
                </div>
                <div class="network">
                    <template is="dom-if" if="{{twitter}}">
                        <a href="{{twitterURL}}" class="fa fa-twitter">@<span>{{twitter}}</span></a>
                    </template>
                    <template is="dom-if" if="{{linkedin}}">
                        <a href="{{linkedinURL}}" class="fa fa-linkedin">{{linkedin}}</a>
                    </template>
                </div>
            </div>
        </div>
    </template>
    <script>
        Polymer({
            is: "v-card",
            properties: {
                email: {
                    type: String
                },
                firstname: {
                    type: String
                },
                image : {
                    type: String,
                    value : "anon.png"
                },
                linkedin: {
                    type: String
                },
                site: {
                    type: String
                },
                surname: {
                    type: String
                },
                twitter: {
                    type: String
                },
                emailURL : {
                    type: String,
                    computed: 'computeEmailURL(email)'
                },
                fullname : {
                    type: String,
                    computed: 'computeFullName(firstname, surname)'
                },
                linkedinURL : {
                    type: String,
                    computed: 'computeLinkedinURL(linkedin)'
                },
                twitterURL : {
                    type: String,
                    computed: 'computeTwitterURL(twitter)'
                }
            },
            computeEmailURL : function (email) {
                return 'mailto:' + email;
            },
            computeFullName : function (firstname, surname) {
                return firstname + ' ' + surname;
            },
            computeLinkedinURL : function (linkedin) {
                return 'https://es.linkedin.com/in/' + linkedin;
            },
            computeTwitterURL : function (twitter) {
                return 'https://twitter.com/' + twitter;
            }
        });
    </script>
</dom-module>

y el navegador muestra/interpreta:

Y nuestros webcomponents ya no mostrarán código de más

Y nuestros webcomponents ya no mostrarán código de más

Webcomponents y Polymer es mucho más

Ya tenemos el primero de muchos de nuestros webcomponents… y además es bastante completito. Sin embargo Polymer da mucho más de sí y en este artículo no hemos hecho nada más que rascar un poco en la superficie… ya iremos desgranando todo su potencial poco a poco 😉

Recursos

4 pensamientos en “Webcomponents: Qué son y su uso con Polymer

Deja un comentario