Single Page Application: Un viaje a las SPA a través de Angular y JavaScript
Repasemos los fundamentos de las corrientes más populares actualmente para el desarrollo de plataformas web
Índice: en este artículo
0- Objetivos
1- Introducción
2- Definiendo SPA: Qué es Single Page Application
2.1- Antecedentes
2.2- Caracterizando una SPA
3- Identificar SPAs: ¿Es Single Page Application o no?
3.1- SPA y SSR
3.2- Diez claves fundamentales de una SPA
4- ¿Cómo implementan las SPAs estas cuestiones?
4.1- Location API
4.2- History API
4.3- Web Storage API
5- Angular como herramienta SPA
5.1- Contexto
5.2- Características básicas de Angular
6- Primer ejercicio con Angular: Hello World
7- Referencias y lecturas recomendadasEste artículo continúa en:
Single Page Application: Un viaje a las SPA a través de Angular y JavaScript(II)Otros artículos no relacionados que podrían interesarte:
Composer y Drush en el contexto de Drupal 8
Form API(I): Comprender, crear y modificar formularios en Drupal 8
Form API(II): Modificando formularios en Drupal8 mediante form_alter
Form API(III): Caso práctico de modificación de formulario Drupal 8
0- Objetivos
En este artículo aprenderás:
1. El concepto de Single Page Aplication (SPA) y sus características.
2. Pilares básicos de como funciona SPA (Location, History, WebStorage).
3. Las posibilidades de Angular como SPA y sus características.
4. Instalar los recursos para trabajar con Angular (CLI, Node, npm).
5. Una mecánica básica para la creación y despliegue de aplicaciones Angular.
1- Introducción
Vivimos un Boom de frameworks, librerías y recursos basados en Javascript y nos encontramos en mitad de una explosión de opciones: React, Angular, Vue, Ember, Backbone, Node, Polymer, la vieja JQuery y por supuesto…Vanilla…¿Qué hacer? ¿Por dónde empezar? ¿Cuántas curvas de aprendizaje podemos afrontar a la vez? ¿De que va todo esto?
Bien, intentaremos poner algo de orden en todo esto. Para empezar en este artículo trataremos el concepto de Single Page Application desde varios puntos de vista para comprender bien a que se debe un término en pleno hype como este, y como puede implementarse con una herramienta como Angular.
¿Por qué Angular? Buena pregunta. En principio React o Vue son opciones igualmente interesantes, pero me ha hecho decantarme por el framework Angular -fuertemente mantenido por Google- el hecho de que en el grupo de ofertas de empleo SevillaDeveloperJobs+ las propuestas de trabajo relacionadas con Angular superan en apariciones a otras keywords como PHP o Java y además aparecen con horquillas salariales muy interesantes. Así que parece ser que a) Hay mercado y b) Hay buenos sueldos en juego.
Razones más que suficientes para decantarse.
2- Definiendo SPA: Qué es Single Page Application
Primero unas ideas básicas sobre de que estamos hablando en realidad…¿Recordáis aquello de las Rich Internet Applications (RIA)? era una visión acerca de llevar la mecánica de una aplicación de escritorio a la web, convertir tu navegador en un entorno de escritorio y esto y lo otro…pues esto sería como un paso más allá en el que ya no necesitas llevar a tu sistema al borde del colapso debido al procesamiento de una enorme bola de flash compilado. Ahora, todo es Javascript. Y todo corre de tu parte.
2.1- Antecedentes
Antes todo esto era campo. Como podemos apreciar en la amable gráfica siguiente, una relación cliente — servidor tendía a ser como el siguiente esquema conceptual, lo que podríamos llamar un escenario SSR (Server Side Rendering): Renderizado del lado del servidor, es decir, la capacidad para construir el etiquetado de páginas y vistas con datos y resultados que se chutarán a navegador y este se limitará a pintar.
Más tarde, aprendimos a aliviar esa carga a través de AJAX, realizando peticiones parciales de AJAX a través de Javascript que servían para actualizar secciones específicas, repintar bloques con datos actualizados, modificar vistas, etc. Esto hacía un SSR modificado, algo más liviano y llevadero:
Y cogiendo el gusto por realizar pequeñas peticiones parciales todo lo que se pueda, llegamos al paradigma SPA: Desacoplamiento MVC donde las vistas son bloques parciales que obtienen o ingresan datos al modelo y se actualizan de manera individualizada:
2.2- Caracterizando una SPA
Para ayudar a clarificar mejor el concepto de SPA, podemos enfocarlo desde diferentes planos, a modo de cortes histológicos. Así, diríamos que:
Inicialmente, podemos empezar asumiendo que SPA son aplicaciones web creadas sobre una sola página que muta mediante interacciones Javascript.
Intuitivamente, podemos decir que es un tipo de aplicación que consiste en mostrar todas sus pantallas en la misma página, sin realizar sucesivas cargas de otras direcciones / secciones de la plataforma provenientes del servidor al cambiar de dirección interna URL.
Conceptualmente, podemos decir también que una Single Page Application o SPA, es un sitio web que recarga y muestra su contenido en respuesta a acciones propias de la navegación (enviar un formulario, clickar en un enlace, acceder a una sección interna…) sin tener que realizar peticiones al servidor para volver a cargar más código HTML. Las aplicaciones web básicas lo hacían por completo, AJAX lo intentaba resolver parcialmente y ahora SPA intenta reducirlo al mínimo necesario.
Filosóficamente, podemos específicar que parte de la filosofía de SPA es ofrecer una experiencia de usuario similar a la de una aplicación de escritorio pero desde un navegador, manejando datos, flujos y estructuras que dependen de una conectividad a Internet en menor medida que el resto de aplicaciones web tradicionales, de una manera ágil y grata, ofreciendo una experiencia de usuario muy cómoda, rápida y agradable.
Y funcionalmente, podríamos describir varias características de una SPA:
Punto de entrada central: Un punto de entrada único, un que se va transformando y adaptando mediante acciones.
Página fija, Vistas cambiantes: Como en el caso de una aplicación de escritorio, nos mantenemos en un “marco único” y fijo, mientras que “vistas dinámicas” van ofreciéndonos las distintas posibilidades del uso y navegación.
Página fija, no URL fija: Es posible que la dirección URL sufra cambios en base a las actividades de uso de la plataforma y vaya modificándose aunque ese “marco único” se mantenga fijado. Esto es un tanto reduccionista (existen SPA que no transforman sus direcciones), pero es útil para comprender su mecánica.
Viajar ligera de equipaje: Las peticiones cliente — servidor tienden a ser más laxas y más livianas que en el caso de una plataforma web al uso. Datos, solo datos. Y además muchos procesos quedan del lado del navegador web del cliente a partir de diversas herramientas (como LocalStorage, por ejemplo). Datos y preferentemente en formato JSON.
Ejemplos de aplicaciones SPA: Gmail, Netflix, Trello, Github…
3- Identificar SPAs: ¿Es Single Page Application o no?
Así, siguiendo este modelo sobresimplificado, es como llegamos al escenario actual. ¿Es SPA la salvación universal, la panacea? No. De hecho estamos en un momento en el que se pone en cuestión la utilidad completa del modelo SPA en implementaciones íntegras, en cuanto a que la mayoría de aplicaciones web que hemos pasado a usar han tendido a ser SPA y por tanto, han terminado reventando nuestros navegadores a base de procesos de trabajos y datos.
3.1- SPA y SSR
Por ello ha ganado terreno el escenario de “Javascript Isomórfico” para “Aplicaciones Universales”, es decir, plataformas que reparten el trabajo entre cliente y servidor, algo a medio camino entre SPA y SSR. Es lo que marca el origen de tecnologías como Node.js y en general, la visión de ejecutar Javascript del lado del servidor. Para conocer mejor este enfoque, recomiendo este irónico y divertido artículo+ de Michael Jackson. En cualquier caso y para no terminar de dislocar una curva de aprendizaje que inicialmente no es fácil, lo reduciremos (de momento) al concepto de SPA.
La cuestión a la que debemos responder es…¿Cómo saber si estamos ante una SPA? (seguramente algo más Isomórfico SPA — SSR, pero simplificamos).
Para ello he reunido diez puntos para checkear:
3.2- Diez claves fundamentales de una SPA
- Lo mejor de ambos mundos: Básicamente, una SPA debe fusionar lo mejor de las dos maneras clásicas de entender el contexto de uso de las aplicaciones. Por un lado la rapidez y la fluidez de una aplicación de escritorio y por otro, la portabilidad de una aplicación web.
- Sin viajes ida-vuelta al servidor: Una SPA que se digne de serlo es capaz de repintar cualquier parte de la UI del cliente sin realizar una petición completa para renderizar una página HTML, separando datos de presentación.
- Reduce los tiempos de respuesta: Si estamos ante una SPA, entonces estamos tratando (o deberíamos) con una reducción de los tiempos de respuesta habituales de la aplicación, debido a la delegación de procesos del servidor al navegador web. Que solo queden en el servidor remoto procesos relacionados con autenticación, validación de datos, o almacenamiento persistente. Ese es el objetivo.
- Reduce el consumo de ancho de banda: Las conexiones de una SPA con un servidor deben ser en pequeños tramos muy puntuales (en terminos generales, serán constantes si hablamos de streaming en VOD, por ejemplo). Pero en general, los datos serán transferidos en objetos manejables tipo JSON.
- Actualizaciones automáticas y distribuidas: Como un website, los usuarios no tienen que realizar ninguna acción para actualizar el software, solo recargar la dirección de la aplicación, sin más.
- Informa de su estado a la persona usuaria: Como en una aplicación de escritorio, reporta a la persona usuaria cuando no está saliendo como se espera, o bien si queda “waiting for”, esperando una respuesta, muestra elementos de UX suficientes como para conocer que ocurre: barras de progreso, mensajes de estado, avisos de errores/esperas, etc.
- Routing eficaz: Igualmente, debe ser capaz de construir bien el seguimiento del estado y localización (state and location) del usuario, a lo largo de su navegación, y más mediante HTML5 History API que por Hashbang Technique.
- Cross-Platform, de verdad de la buena: Queriendo ser una aplicación de escritorio pero corriendo en el navegador, al margen de sistema operativo, disponible para cualquier dispositivo. Tablets, smartphones, portátiles, sobremesa, televisiones inteligentes, consolas de videojuegos…para todos los dispositivos.
- Flexibilidad y rendimiento: Una SPA que quiera ser competitiva, debe transferir todo lo relacionado con Interfaz de Usuario al cliente, vía Javascript, para reducir el consumo de ancho de banda y las respuestas offline de la plataforma funcionando “a red caida” ;-)
- Facilidad para desconexión — conexión: Como señalábamos en el item anterior, una SPA debe resultar grata al perder conectividad, ok. Pero es que también debe ser responsable de realizar todas las sincronizaciones necesarias entre cliente — servidor en cuanto se restablezca la conexión a la red.
4- ¿Cómo implementan las SPAs estas cuestiones?
Bien, aunque la respuesta a esta pregunta es bastante extensa y podría dinamitar el alcance ya de por si majareta de este artículo, intentaremos asirnos al menos a algunos aspectos técnicos fundamentales que debemos conocer para operar sobre SPAs. Me refiero a los recursos que permiten implementar todas esas abstracciones que venimos comentando en apartados anteriores: que si routing, que si gestión de direcciones, que si delegación a procesamiento en navegador cliente…muy bien,…
¿Pero cómo se hace? veamos tres piezas fundamentales del ensamblado de una SPA, tres recursos básicos proporcionados por la relación entre HTML5 y Javascript para realizar este tipo de tareas. Todas se sirven del BOM (Browser Object Model) para poder relacionarse con el navegador gracias a los métodos de sus APIs. Las tres APIs elementales para hacer SPA:
4.1- Location API
Se encarga de procesar información acerca de la ubicación de objetos dentro del sistema recogiendo parámetros de la dirección URL.
https://developer.mozilla.org/en-US/docs/Web/API/Location+
La API de Ubicación es importante a la hora de gestionar contenidos en base a los parámetros de la dirección URL que maneje nuestra SPA, para lo cual, en primer lugar tendríamos que conocer la anatomía de una dirección URL y como está se parsea y transfiere a un Objeto de tipo window.location:
De todos estos parámetros, para una SPA estándar solo tres son fundamentales:
- pathname: es la propiedad que determina que tipo de contenido renderizar.
- search (query string): esta propiedad marcará que sub — tipología de los contenidos señalados por pathname se mostrarán de manera específica (si existen).
- hash: por último, esta propiedad marca una posición (vía anchor) sobre los elementos a renderizar.
Normalmente, la dirección URL es una de las principales maneras en las que la persona usuaria interactúa con la plataforma. En el caso de las SPAs, se usa la propiedad window.location, que nos permite gestionar información, así como describir interacciones entre las diferentes partes de una dirección URL.
4.2- History API
Es una colección de métodos con la que los navegadores te permiten interactuar tanto con la barra de direcciones como con el historial de navegación.
https://developer.mozilla.org/en-US/docs/Web/API/Window/history+
Esta herramienta sirve, fundamentalmente, para operar sobre movimientos de navegación, llevando las vistas hacia delante o hacia atrás en base al registro que el navegador mantiene para esa ventana activa en concreto, donde el valor de la página mostrada actualmente (la vista renderizada) se gestiona a través de la propiedad history.state
(reúne la posición en el índice de Historia y el valor):
Para ello utiliza varios métodos que generan movimientos en la navegación:
history.go(n) - Carga una página a N saltos de navegación
history.back() - Renderiza justo la vista anterior a la actual
history.forward() - Renderiza justo la vista siguiente a la actual
history.pushState(state, null, pathname) - Introduce nuevo elemento
history.replaceState(state, null, pathname) - Sustituye State actual
Ejemplo de carga en Historia a través de PushState():
const state = { foo: 'bar' }
history.pushState(state, '', '/foo')
Por motivos de seguridad, no se permite que obtengamos (directamente) un set de direcciones provenientes del history en formato array o JSON con dicho histórico que podamos manipular, pero a saltos y avances podemos marcar navegaciones hacía atrás o adelante.
4.3- Web Storage API
Proporciona mecanismos de almacenamiento de datos en formato clave/valor dentro del navegador web.
https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API+
Los pilares de Web Storage son dos: Local Storage & Session Storage. LocalStorage y SessionStorage son propiedades de HTML5 (web storage)para almacenar datos en nuestro navegador web de manera parecida a como lo hacen las cookies. Las características de Local Storage y Session Storage son:
- Permiten almacenar entre 5MB y 10MB de información; incluyendo texto y multimedia.
- La información está almacenada en la computadora del cliente y NO es enviada en cada petición del servidor, a diferencia de las cookies.
- Utilizan un número mínimo de peticiones al servidor para reducir el tráfico de la red.
- Previenen pérdidas de información cuando se desconecta de la red.
- La información es guardada por dominio web (incluye todas las páginas del dominio).
Local Storage Guarda información que permanecerá almacenada por tiempo indefinido; sin importar que el navegador se cierre.
Session Storage Almacena los datos de una sesión y éstos se eliminan cuando el navegador se cierra.
Para probarlos, comos sus métodos son similares, nos bastará hacer pruebas con LocalStorage.
Comprobar la disponibilidad de Local Storage en navegador:
// Testing if there is a local storage in browserif(typeof(Storage) !== 'undefined'){
console.log("Local Storage is available :-)");
}else{
console.log("Local Storage is not compatible :-( ");
}
Cargar elementos en Local Storage a través de SetItem():
// Getting elements from screen and load in Local Storage var title = document.querySelector('#addfilm').value;
if(title.length >= 1){
localStorage.setItem("title", title);
}
Ahora getItem():
// Setting elements in screen through DOMdocument.querySelector("#films").innerHTML = localStorage.getItem("title");
Y removeItem():
// Deleting elements in Local Storage var name = document.querySelector('#deletefilm').value;
if(name.length >= 1){
localStorage.removeItem(name);
}
Aquí en esta rama de un repo de gitlab tengo un par de ejercicios muy intuitivos sobre el uso de Local Storage, por si te apetece echarle un vistazo:
https://gitlab.com/davidjguru/master-javascript/tree/js_JSON/js_JSON_Folder+
¿Crees que estás partes tienen sentido? las irán teniendo, sin duda. Tal vez ahora no parezcan lo suficientemente relacionadas entre sí, pero hay que tener en cuenta que con estas tres piezas y un relativo conocimiento de Javascript, podemos ya articular un sistema de Routing para construir la gestión de contenidos de nuestra plataforma SPA. Llegaremos.
5- Angular como herramienta SPA
Al margen de la teoría que hemos revisitado y de nociones de APIS para interactuar con el navegador desde nuestra aplicación SPA basada en Javascript, creo que puede ser útil tocar un poco, una aproximación a una herramienta popular para la construcción de este tipo de plataformas.
5.1- Contexto
En sus inicios, fue una librería Javascript lista para integrar y usar pero terminó siendo reconstruida como framework. Tras el refactoring (y casi que replanteamiento integral) a partir de sus primeras versiones (llamadas AngularJS) a las posteriores, que han sido llamadas simplemente Angular con su nuevo nombrado numérico aproximado al modelo SEMVER (https://semver.org+), Angular se ha convertido en una herramienta de referencia para crear SPAs. Al parecer, no tiene una ligazón muy sencilla para crear “Universal Apps” en el sentido de que ofrece un maridaje complejo SPA + SSR, pero su cuota de mercado sigue siendo bastante fuerte. Así que la tomaremos como referencia al menos para frontend — SPA.
Como hemos comentado anteriormente, la carga -especialmente la primera petición cliente/servidor- consistía para aquella versión inicial en la descarga integral de toda la plataforma (páginas, vistas, rutas, componentes, etc) inicialmente, lo que penaliza al visitante ocasional (para el resto de visitas ya los recursos deberían encontrarse debidamente cacheados). En el caso de Angular 1 (Angular.js), causaba una sobrecarga letal en el navegador del visitante y los intentos de implementar “Lazy Load” (carga perezosa) al parecer no dieron los frutos esperados.
Angular se maneja en TypeScript, que genera una transpilación posterior a un Javascript procesable por un navegador web.
5.2- Características básicas de Angular
Lazy SPA: Inyector de dependencias de Angular que no necesita que estén en memoria todo el código de los elementos de una aplicación. Con Lazy SPA el framework puede funcionar sin conocer el resto del código, cargándolo de manera dinámica.
Renderizado Universal: Aunque la intención inicial era renderizar HTML en el navegador, este enfoque permite ahora terminar renderizando una vista al código nativo específico de un dispositivo.
Data Binding Flow: Esto es, básicamente, la capacidad para sincronizar de manera automática los datos entre modelo y vista, como un enlace entre front y back en nuestro proyecto. La vista siempre mostrará los datos que contiene el propio modelo: si el modelo cambia, la vista se actualiza (retiramos un elemento de una tabla de nuestra base de datos en backend, por ejemplo, y este elemento deja de estar visible en pantalla) Pero ahora se genera una situación “Two-Way Data Binding”, es decir, además de este ejemplo descrito hace un par de líneas, también si modificamos la Vista, el Modelo se altera. Ahora la influencia de uno sobre el otro es totalmente (o casi) una relación biyectiva.
Componentes: Es un concepto muy importante en Angular, y consiste en ser, esencialmente, el elemento base de la arquitectura de un proyecto Angular. Construir componentes es obligatorio a partir del refactoring de Angular2 y tiene por intención mejorar la encapsulación de elementos y funciones.
Angular CLI: Angular Command Line Interface, la herramienta de línea de comandos basada en Node.js (un entorno de ejecución de Javascript) para trabajar con Angular. Un recurso esencial para (entre otras muchas cosas) hacer scaffolding (andamiaje)de un proyecto y sus componentes. Será importante tener en tu sistema Node.js y el gestor de paquetes npm convenientemente instalados.
6- Primer ejercicio con Angular: Hello World
Para comenzar a operar con Angular, es fundamental poder disponer de Node.js y npm en tu equipo: Tanto Angular en sí, como Angular CLI y las aplicaciones Angular en general dependen de funcionalidades y recursos que son proporcionadas por librerías ofrecidas por un proveedor (vendor) en forma de paquetes manejados por el gestor de dependencias npm (como ocurre en el caso de Composer para quienes venimos del entorno PHP).
Ok, empecemos.
Primer paso: Instalar Node.js y npm en el equipo
Es posible que en tus repositorios oficiales las versiones estén algo anticuadas, por lo que es mejor gestionar la instalación de la última versión disponible de Node. Aquí podéis consultar las últimas versiones disponibles y útiles para la descarga: https://nodejs.org/en/+
Y en el momento de escribir este artículo, tenemos disponibles las versiones LTS (Long Term Support, para mucho tiempo) y Actual (Última versión disponible):
Bien para empezar, asegúrate que tienes instalado curl en tu sistema. Curl+ es una herramienta muy popular (la instrucción git clone -por ejemplo- tira de curl para copiar proyectos vía git en tu equipo) de transferencia de recursos entre equipos que soporta más protocolos que wget y dispone de una librería de referencia (libcurl+) para poder integrar en tus proyectos. Así que instalamos curl si no lo tenemos ya:
$ sudo apt-get install curl
Con curl ya disponible, volvemos al punto anterior: vamos a buscar el paquete de Node que más nos interese al margen de nuestros repositorios oficiales.
El siguiente paso es integrar el PPA (Personal Package Archive, repositorio de software disponible) de Node en tu sistema como origen de software. Como la instrucción apt de instalación no discrimina un repositorio sobre otro, simplemente a la hora de instalar consultará dónde está la versión más actualizada o justo la solicitada y la instalará, sin más.
Añadimos los datos del PPA de Node gracias a esta instrucción vía curl:
$ curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash -
Esta instrucción descarga un fichero (https://deb.nodesource.com/setup_11.x+) de consulta, configuración y actualización de los datos del repositorio de la versión 11_x de Node y justo después al recibirlo envía el resultado como parámetro a la siguiente instrucción (tras “|” ) que lo ejecuta vía bash.
Importante: Recuerda que en el repositorio github del proyecto, tienes información disponible sobre versiones y procesos de instalación en casi todas las distribuciones de Linux: https://github.com/nodesource/distributions+
A continuación, con la información ya integrada, podemos lanzar la instalación de Node (confirmada por defecto con el parámetro -y incluido en la instrucción):
$ sudo apt-get install -y nodejs
Esta instrucción instalará Node y npm+ en tu equipo. Ahora comprueba la versiones instaladas a través del terminal para confirmar que todo ha ido “ok”:
$ node -v
$ npm -v
Ahora instalamos Angular-cli a través de npm con la típica estructura de petición vendor/package (como en Composer):
$ sudo npm install @angular/cli -g
Tras la instalación, comprobamos la versión de Angular Cli en nuestro sistema:
$ ng version
Creamos un nuevo proyecto basado en Angular:
$ ng new project-testing
Que lanzará un par de preguntas antes de lanzar todo el proceso de creación de un nuevo proyecto y el scaffolding básico de recursos para este:
Ahora entramos a la nueva carpeta del proyecto y vemos la estructura básica:
$ cd project-testing/ && ls
A continuación solicitamos la transpilación del proyecto y la apertura del mismo a través del navegador con una instrucción que admite variantes:
$ ng serve // Dispone el proyecto en localhost:puerto
$ ng serve --open // Abre el navegador con el proyecto visible
$ ng serve --port 5050 // Modifica el puerto por defecto (4200)
Et voila! en la dirección localhost:4200 tendremos nuestro particular Hello World creado con Angular:
Y con esto tenemos disponible este pequeño Hello World inicial con Angular.
Aquí tenéis el resumen de comandos en formato snippet de gitlab+ o bien en formato gist de Github+. Resumiendo:
¡Salud!
7- Referencias y lecturas recomendadas
- “Angular Framework: SPA xor SSR”, by Pablo Bermejo
https://dev.to/peibolsang/angular-framework-spa-xor-ssr-339o+ - “Why Everyone is Talking About Isomorphic / Universal JavaScript and Why it Matters”, by Azat Mardan
https://medium.com/capital-one-tech/why-everyone-is-talking-about-isomorphic-universal-javascript-and-why-it-matters-38c07c87905+ - “Universal Javascript” by Michael Jackson
https://cdb.reacttraining.com/universal-javascript-4761051b7ae9+ - “How I Implemented my own SPA Routing System in Vanilla JS” by Bryan Manuele (Fermi Dirak)
https://medium.com/@bryanmanuele/how-i-implemented-my-own-spa-routing-system-in-vanilla-js-49942e3c4573+