Analizamos con ejemplos sencillos los componentes app-location y app-route, para comenzar a comprender cómo se interpretan las rutas de aplicaciones SPA en Polymer.
Con Polymer nos encontramos ante una librería de Web Components, pero lo cierto es que su ámbito se acerca a lo que nos ofrece un framework, sobre todo por su amplia colección de elementos disponibles. Uno de los ejemplos es el sistema de routing.
Lo más espectacular, a mi juicio, es que en Polymer han conseguido modelar el sistema de routing basándonos en etiquetas (componentes) que podemos configurar de manera declarativa. Mientras que otros sistemas requieren el uso de Javascript para poder definir el sistema de routing, aquí vamos a necesitar editar simplemente el HTML. Quizás se pueda decir que ésto no es más que la filosofía intrínseca de los Web Components, pero lo cierto es que a mi no me deja de sorprender.
Vamos a ver en este artículo cómo está compuesto el sistema de routing y cómo configurarlo. Pero antes y por si alguien no lo sabe todavía, el sistema de routing permite en páginas del tipo "Single Page Application" (SPA) disponer de rutas internas a secciones concretas de la aplicación. En las SPA todas las ventanas de la aplicación en realidad son la misma página con vistas que se van intercambiando, por decirlo de otra manera, todo se produce dentro del index.html. Sin embargo, por diversos motivos es interesante que nuestros usuarios puedan entrar directamente en una página interna, como la ficha de un producto, el perfil de un usuario o la página de contacto. Esto se consigue con rutas, URL internas que se escriben en la barra de direcciones del navegador, las cuales la aplicación es capaz de interpretar para presentar al usuario la pantalla correcta dentro de la aplicación.
Componentes que forman el sistema de routing
Comencemos analizando los componentes que forman parte del sistema de routing y con los que podemos conseguir las deseadas rutas en una SPA. Son dos:
app-location
Este componente permite analizar la barra de direcciones del navegador, generando un objeto que modeliza el estado actual de la barra, es decir, la URL que hay escrita. Es capaz de exponer hacia el sistema de binding de Polymer el estado de la barra de direcciones y así mismo, en el momento en el que el objeto cambia dentro de la aplicación, repercutir ese estado hacia la barra de direcciones.
Por decirlo de otra manera, app-location hace de puente entre la barra de direcciones y la aplicación, por medio de un atributo llamado "route". Si se actualiza la barra de direcciones app-location cambia el valor del objeto "route" y si dicho objeto cambia, app-location actualiza el estado de la barra.
app-route
El componente app-route toma el valor de la ruta proveído por el app-location y lo descompone, de modo que nos resulte sencillo analizarlo, exponiendo el estado de la ruta al sistema de binding de manera totalmente personalizada por el desarrollador.
Básicamente en app-route vamos a definir un patrón (pattern) que se puede buscar en las rutas y si ese patrón está presente descompone la dirección en varias partes que nos permitan su análisis y utilización dentro de la aplicación. Luego entraremos en detalle, pero esa descomposición producirá dos objetos, uno de datos encontrados en el patrón (atributo data) y otro con datos que no se llegaron a analizar en el mismo (atributo tail).
Mientras que sería lógico tener solo un app-location por aplicación, es normal que tengamos varios app-route para descomponer los datos de la URL atendiendo a varios patrones posibles de rutas.
Antes de comenzar a usar estos componentes, como ya debes saber, debemos instalarlos dentro de nuestro proyecto, con el correspondiente comando de bower. Se instalan ambos en un solo comando, ya que forman parte del mismo bundle.
bower install --save PolymerElements/app-route
A continuación vamos a hacer algunos ejemplos simples que nos permitan entender estos componentes de manera práctica y sencilla.
Obviamente, también tendrás que hacer los correspondientes import, para que tu aplicación sea capaz de reconocerlos.
<link rel="import" href="../bower_components/app-route/app-location.html">
<link rel="import" href="../bower_components/app-route/app-route.html">
Ejemplo con app-location
En nuestro primer ejemplo vamos a trabajar únicamente con el componente app-location, para reconocer la ruta y saber cómo es el objeto que te ofrece para modelizarla.
Para usar este componente solamente necesitamos un código como este:
<app-location route="{{route}}"></app-location>
Como ves, el atributo "route" te ofrece hacia el sistema de binding un valor, que es un objeto donde podremos encontrar información sobre la ruta.
Para poder experimentar con app-location en este ejemplo vamos a comenzar colocando dos botones sencillos que hagan cosas.
<button on-tap="mostrarRoute">route a consola</button>
<button on-tap="cambiarRoute">cambiar route</button>
Primero tenemos un botón que, pulsado, enviará a la consola el objeto de la ruta.
El manejador de evento del primer botón es el siguiente método del componente:
mostrarRoute: function() {
console.log(this.route);
},
Si estamos en la ruta raíz de la aplicación, el objeto de la ruta tendría esta forma.
En cuanto, si estamos en la ruta "/productos" tendría esta otra forma.
Como puedes ver, el "path" ha cambiado. También tiene un atributo "prefix" que está siempre a vacío. Más adelante veremos para qué sirve este "prefix".
Ahora vamos a ver el otro manejador de eventos, para el segundo botón. Básicamente lo tenemos para modificar programáticamente el valor del objeto "route". Como sabes, al modificarlo, repercutirá en el estado de la ruta en la barra de direcciones.
cambiarRoute: function() {
this.set('route.path', '/otra/66');
},
Al cambiar la propiedad "path" del objeto "route" el componente app-route se debe encargar de modificar la barra de direcciones, viajando a la ruta "/otra/66". Si luego volvemos a inspeccionar el valor del objeto route en la consola nos dirá que su path a cambiado y lo podremos ver también reflejado en URL a la que el navegador está situado.
El código completo de este ejemplo lo puedes ver a continuación.
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/app-route/app-location.html">
<dom-module id="test-app-location">
<template>
<style>
:host {
display: block;
}
</style>
<h1>Test APP-LOCATION</h1>
<app-location route="{{route}}"></app-location>
<button on-tap="mostrarRoute">route a consola</button>
<button on-tap="cambiarRoute">cambiar route</button>
</template>
<script>
Polymer({
is: 'test-app-location',
properties: {
route: Object
},
mostrarRoute: function() {
console.log(this.route);
},
cambiarRoute: function() {
this.set('route.path', '/otra/66');
},
});
</script>
</dom-module>
Ejemplo con app-route
Para acabar con esta primera zambullida en el sistema de routing de Polymer vamos a conocer el componente app-route, que se encarga de interpretar el objeto "route" generado por el "app-location" y exponer el valor de la ruta en caso que se ajuste a un patrón.
Para que ambos componentes funcionen juntos es necesario bindear el objeto "route" de un componente a otro, con binding de dos direcciones, de modo que los cambios se entreguen de una parte a la otra.
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}}"
pattern="/:page"
data="{{data}}"
tail="{{tail}}">
</app-route>
El componente app-route tiene otra serie de propiedades que debemos explicar:
- route: Es el objeto de la ruta, que nos ha generado el app-location.
- pattern: Es el patrón buscado en la ruta. Si lo que hay escrito en la barra de direcciones concuerda con este pattern, entonces app-route descompondrá esa URL en varios pedazos.
- data: Todos los datos que se ajusten a la definición del patrón, se guardarán en el objeto data. Nuestro patrón se definió como "/:page", lo que quiere decir que el segmento que hay detrás de la raíz de la aplicación se meterá en el objeto "data" con el atributo "page".
- tail: Es todo lo que no se ha reconocido en el patrón anterior. Todo aquello que sobró, se coloca dentro del objeto tail, el cual nos vendrá muy bien para hacer rutas profundas de aplicaciones, pero que no vamos a analizar todavía en este artículo.
Nuestro componente de ejemplo tiene ahora un botón para mostrar qué hay en el objeto "data" producido.
<button on-tap="mostrarData">Data a consola</button>
El manejador de eventos hace un simple "console.log" para enviar el objeto "data" a la consola.
mostrarData: function() {
console.log('Data:', this.data);
},
Ahora podemos experimentar con diversas rutas y ver lo que pasa.
Ruta raíz "/"
La llamamos la ruta "/" pero lo más seguro es que en tu barra de direcciones tengas la palabra localhost o cualquier otro dominio como "http://localhost:8080/". En este caso la ruta concuerda con el patrón, aunque "page" tiene un valor vacío.
Ruta /agenda/
Esta ruta concuerda con el patrón. Entonces data se llenará con el valor del patrón reconocido.
Ruta /productos/
Como en el caso de antes, sí concuerda con el patrón y podemos recuperar en data.page el valor de la página "productos".
Ruta /productos/45/
En esta nueva ruta tenemos también el patrón "/:page", aunque hay más datos a reconocer que van más allá del propio patrón. El objeto "data" seguirá teniendo el mismo valor, que en el caso anterior.
Pero ahora el tail, del que no hemos hablado mucho todavía, contendrá alguna información extra, con toda la parte de la URL no reconocida con el patrón.
Ahora te dejamos el código completo del segundo componente de ejemplo, para ver el app-route en funcionamiento.
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/app-route/app-location.html">
<link rel="import" href="../bower_components/app-route/app-route.html">
<dom-module id="test-app-route">
<template>
<style>
:host {
display: block;
}
</style>
<h1>app eit</h1>
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}}"
pattern="/:page"
data="{{data}}"
tail="{{tail}}">
</app-route>
<button on-tap="mostrarData">Data y tail a consola</button>
</template>
<script>
Polymer({
is: 'test-app-route',
properties: {
route: Object,
},
mostrarData: function() {
console.log('Data:', this.data);
console.log('Tail:', this.tail);
},
mostrarTail: function() {
},
});
</script>
</dom-module>
Conclusión
Hemos comenzado a entender el sistema de routing de Polymer con ejemplos muy simples. De momento solo nos hemos preocupado por conocer los dos componentes que usaremos para trabajar las rutas de la aplicación, aunque quizás sea un misterio todavía cómo usarlos para poder intercambiar las vistas de tu app.
Todavía tendremos que estudiar un poco más también para poder reconocer rutas complejas, pero ya será en la próxima entrega.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...