Cómo crear tu propio Servidor VPN con OpenVPN

OpenVPN es una solución multiplataforma que ha simplificado la configuración de VPNs en contra de soluciones más antiguas y difíciles de configurar como IPSec, haciéndolo más accesible para el público con menos experiencia en este tipo de tecnologías, permitiéndonos así crear nuestro propio servidor VPN.

Servidor

Lo primero que tenemos que hacer para crear nuestro propio servidor VPN es, obviamente, conseguir un servidor dónde realizar la instalación. Existen múltiples opciones en la nube y la mayoría de ellas, además, cuentan con imágenes para poder preinstalar OpenVPN en la creación de los servidores. Entre las posibles opciones que podemos utilizar encontramos los siguientes:

  • ScaleWay C1: 4 CPU, 2GB RAM, 50GB SDD, 1 IP, 200 Mbit/s ancho de banda -> 2.99€ al mes.
  • DigitalOcean: 1 CPU, 512 MB RAM, 20 GB SDD, 1 IP, 1 TB ancho de banda -> 5$ al mes.
  • Google Cloud Platform: Facturación por minutos de uso.
  • Amazon EC2: Facturación por minutos de uso.

También podríamos realizar la instalación en cualquier máquina Ubuntu con acceso a internet, como un ordenador antiguo o una Raspberry Pi. En estos casos deberemos realizar además tareas de redireccionamiento de tráfico en nuestra red local para que todo funcione correctamente.

Instalación

Para instalar OpenVPN en Ubuntu podemos utilizar apt-get mediante el siguiente comando:

Después de esto, tenemos que extraer el fichero de ejemplo de configuración en la carpeta /etc/openvpn para incorporarlo así a nuestra configuración. Esto se puede hacer con un solo comando:

Una vez extraído editamos el fichero server.conf y realizamos algunos cambios.

Las modificaciones más importantes a realizar son descomentar algunas líneas eliminando el caracter ‘;’. Para que el servidor VPN pase el tráfico web de los clientes a su destino, debemos descomentar la siguiente línea:

Es importante utilizar el servidor OpenDNS con los clientes conectados para la resolución de DNS. Esto puede ayudar a evitar que las solicitudes de DNS se escapen fuera de la conexión VPN. Sin embargo, es importante especificar también estos servidores DNS en los dispositivos de los clientes.. Para lograrlo necesitamos descomentar las siguientes líneas:

Para evitar que OpenVPN se ejecute en modo root descomentaremos las siguientes líneas que especifica el usuario nobody como el usuario por defecto a utilizar. Nobody es un usuario sin los privilegios por defecto de un usuario normal.

El siguiente paso es configurar el reenvío de paquetes con sysctl. Esto le indicará al núcleo del servidor que reenvíe el tráfico de los dispositivos cliente hacia Internet. De lo contrario, el tráfico se detendrá en el servidor. Para lograrlo debemos ejecutar este comando:

Para hacer que este cambio sea permanente debemos descomentar la siguiente línea en el fichero /etc/sysctl.conf.

Cortafuegos

De forma predeterminada, las reglas de reenvío de Linux eliminan todos los paquetes que intentan acceder a otra interfaz, por lo que necesitamos configurar un cortafuegos que permita el tráfico a través del puerto VPN 1194 y enviar por defecto las peticiones realizadas a Internet por los clientes. Para evitar el uso de IPTables vamos a usar ufw con los siguientes comandos para permitir conexiones SSH y 1194/udp:

Después de eso, tenemos que modificar el archivo de configuración ufw para establecer la política de forward por defecto a ACCEPT. Este archivo es /etc/default/ufw y necesitamos modificar la línea:

Finalmente, debemos añadir unas cuantas reglas en el fichero /etc/ufw/before.rules para que OpenVPN funcione correctamente. Estas reglas son:

El resultado del fichero debe parecerse al de la Captura 1:

Captura 1: /etc/ufw/before.rules

Con los cambios realizados en ufw, podemos activarlo finalmente:

Generación de Certificados

El siguiente paso es configurar y generar los certificados que OpenVPN utilizará para cifrar el tráfico. Para este propósito vamos a utilizar los scripts de Easy RSA en nuestra carpeta de servidor.

Copiamos los scripts de generación de Easy RSA con el siguiente comando:

Creamos una carpeta para almacenar las claves:

Ahora debemos modificar algunas variables en el fichero /etc/openvpn/easy-rsa/vars como la carpeta de EASY_RSA el valor del KEY_SIZE como podemos ver en la Captura 2:

Captura 2: /etc/openvpn/easy-rsa/vars

Antes de crear cualquier clave necesitamos configurar nuestro servidor como CA (Certificate Authority) para firmar nuestras claves con los siguientes comandos:

Como podemos ver en la Captura 3 el sistema nos pedirá información sobre el certificado como el país, la provincia, el nombre, etc.

Captura 3: Creando Autoridad de Certificación

Con el siguiente comando generamos las claves del servidor para un servidor con nombre ‘server’ como podemos ver en la Captura 4.

Captura 4: Generando Clave para el Servidor

Ahora que tenemos las claves en la carpeta/etc/openvpn/easy-rsa/keys/ las copiamos en la carpeta /etc/openvpn:

Generamos además los parámetros Diffie-Hellman. Esto puede tardar varios minutos.

Captura 5: Generando Diffie-Hellman

Finalmente, podemos iniciar nuestro servidor OpenVPN con el siguiente comando:

Claves de Cliente

Una vez generadas las claves del servidor, podemos generar claves para cada uno de los diferentes clientes que queremos conectar a nuestra VPN. No existe ninguna relación entre la clave y el propio dispositivo por lo que podríamos generar una clave de cliente única y usarla en todos los dispositivos, eso sí, sólo un cliente podría conectarse a la VPN al mismo tiempo.

Para crear credenciales de autenticación independientes para los clientes (Captura 6) necesitamos usar el script build-key de la carpeta Easy RSA:

Captura 6: Creando Clave para Client 1

Este comando va a generar las siguientes claves:

  • /etc/openvpn/easy-rsa/keys/client1.crt
  • /etc/openvpn/easy-rsa/keys/client1.key

Ahora que tenemos las claves, podemos proceder con la creación del archivo de configuración. Para ello partimos de uno de los archivos predeterminados de Easy RSA copiándolo en nuestra carpeta:

El siguiente paso requiere descargar a nuestro equipo local las llaves del cliente, los archivos de client.ovpn y ca.cart. Podemos hacer eso con SCP:

Una vez que hayamos descargado los archivos en nuestro ordenador, podemos crear un perfil OpenVPN unificado mediante la edición del archivo de plantilla client.ovpn e incluir la Autoridad de Certificación de los servidores, el certificado de clientes y su clave. También necesitamos hacer algunas modificaciones en el archivo:

  • Descomentar user nobody y group nogroup
  • Cambiar la línea remote my-server-1 1194 line con remote [SERVER-IP] 1194
  • Comentar las líneas de la Captura 7.
Captura 7: Fichero client.ovpn

Para combinar los archivos individuales en el único perfil unificado, el contenido de los archivos ca.crt, client1.crt y client1.key se pegan directamente en el perfil de .ovpn utilizando una sintaxis básica similar a XML. El código XML al final del archivo debe tener este formato:

Clientes OpenVPN

Para poder usar el archivo .opvn de configuración y conectarnos remotamente a nuestro servidor VPN podemos hacer uso de diferentes clientes VPN disponibles.

OpenVPN Connect ofrece versiones para PC, dispositivos Android e iOS. En nuestro caso vamos a utilizar otro cliente VPN para Mac OSX, llamado Tunnelblick. Una vez instalado solo necesitamos abrir nuestro archivo de perfil .ovpn y Tunnelblick configurará automáticamente la conexión VPN para que podamos utilizarla como podemos ver en Captura 8.

Captura 8: Tunnelblick Conectado

Podemos asegurarnos de que estamos conectados a la VPN usando cualquier servicio web para obtener nuestra IP pública como podemos ver en Captura 9 que detecta que todo está bien configurado.

Captura 9: Comprobando mi IP
0

XSLT en Python: lxml

En el artículo Transformaciones con XSLT vimos una introducción al lenguaje de transformaciones, sin embargo, no explicamos como utilizar debidamente esta tecnología. En este artículo explicaremos como realizar transformaciones XSLT en Python haciendo uso de la biblioteca lxml y veremos como montar un servidor web con Flask para hacer uso de nuestro procesador XSLT desde cualquier lugar.

Introducción

Tanto los documentos XML como las hojas de estilo en XSLT son completamente independientes de la plataforma que se vaya a utilizar para realizar las transformaciones. Podemos enlazar una hoja de estilos XSLT a un documento XML de forma permanente añadiendo un elemento <?xml-stylesheet ..?> justo después de la declaración XML como podemos ver en el Fragmento de Código 1. De esta forma, al abrir el documento con un navegador web, éste aplicará las transformaciones pertinentes de forma automática y podremos comprobar el resultado.

Sin embargo, si lo que queremos es generar documentos independientes es necesario utilizar un procesador XSLT que se encargue de aplicar al documento XML las reglas de transformación incluidas en la hoja de estilo XSLT y genere un documento final. La transformación se realiza de la siguiente forma:

  1. El procesador analiza el documento y construye el árbol del documento.
  2. El procesador recorre todos los nodos desde el nodo raíz, aplicando a cada nodo una plantilla, sustituyendo el nodo por el resultado.
  3. Cuando el procesador ha recorrido todos los nodos, se ha terminado la transformación.

Afortunadamente existen procesadores XSLT para casi todos los lenguajes de programación, como Perl, C, Java, etc. En nuestro caso hemos optado por implementar el procesador haciendo uso del lenguaje Python y su librería libxml.

Python

Python es un lenguaje muy potente y fácil de aprender. Maneja eficientes estructuras de datos de alto nivel y cuenta con un enfoque simple pero efectivo de programación orientada a objetos. Posee una sintaxis muy elegante y tipado dinámico, que junto con su carácter interpretado lo convierte en un lenguaje ideal para desarrollo rápido de aplicaciones en innumerables áreas. Python se encuentra en un gran número de aplicaciones web y de escritorio, por lo que es un lenguaje de aprendizaje casi obligatorio para un desarrollador actual, ya que permite resolver problemas de una forma muy eficiente en un corto espacio de tiempo. Además, cuenta con un repositorio de paquetes que permiten extender la funcionalidad de las aplicaciones de una forma sencilla, muy similar a como se hace con NodeJS.

El gestor de paquetes recomendado para Python se denomina pip, y se utiliza de un modo similar a npm o apt-get. Con él, podemos instalar el paquete lxml que nos permitirá procesar documentos XML y HTML de forma sencilla.

En el Fragmento de Código 2 podemos ver cómo aplicar una plantilla XLST a un documento XML como el que vimos en el Fragmento de Código 1.

Como podemos observar, aunque no hayamos visto el lenguaje Python hasta ahora, simplemente leyendo el código entendemos perfectamente lo que hace. Inicialmente se lee el fichero de entrada que se pasa como parámetro al método transforma_documento. Seguidamente se utiliza el contenido del fichero para obtener el árbol de nodos del documento XML y almacenarlos en la variable dom. A continuación se obtiene un objeto de transformación leyendo el fichero de hoja de estilos “hojaEstilos.xslt” en el que se han definido las transformaciones, cuyo contenido podemos ver en el Fragmento de Código 3. Se aplican las transformaciones en el árbol de nodos y se obtiene un nuevo árbol de documento que podemos convertir en una cadena de texto para devolverla como resultado para, si quisiéramos, escribirla en un nuevo fichero obteniendo finalmente nuestro fichero transformado.

Así, si abriéramos el fichero XML en un navegador obtendríamos un resultado parecido al que podemos observar en la siguiente captura.

Transformación XSLT de documento XML a contenido HTML

Flask

Python puede instalarse en cualquier sistema operativo actual. Sin embargo, si queremos utilizar código en este lenguaje desde un entorno web se hace necesario utilizar una infraestructura apropiada que permita desarrollar aplicaciones web o servicios sin necesidad de manejar detalles de bajo nivel como protocolos o manejo de procesos e hilos.

Entre las numerosas infraestructuras que podríamos utilizar nos hemos decantado por Flask, una infraestructura web minimalista que nos permite realizar peticiones a nuestro código de transformación de forma sencilla mediante el desarrollo de una API REST. En el Fragmento de Código 4 podemos ver un ejemplo sencillo de cómo podríamos utilizar nuestro código de transformación visto en el Fragmento de Código 2 utilizándolo como una biblioteca denominada publisher, que se corresponde con el nombre del fichero en el que hemos almacenado nuestras funciones transformadoras.

Para poder gestionar correctamente el servidor web, utilizamos las funciones render_template y request, con el objetivo de devolver ficheros estáticos, y de manejar las peticiones respectivamente. Como podemos observar es un código relativamente sencillo que nos permitirá obtener un documento transformado a partir de un documento que podemos enviar mediante un formulario HTML como el del Fragmento de Código 4.

Podéis encontrar el código fuente utilizado en este artículo en GitHub.

Conclusión

Utilizar Flask nos permitirá, entre otras cosas, poder mejorar la modularidad de un sistema en diferentes máquinas en caso de que fuera necesario, es decir, podríamos perfectamente instalar nuestro servicio de transformaciones en un servidor, y la aplicación web que utilice éste en otro distinto. Sin embargo, si quisiéramos utilizar tanto Flask como NodeJS en una misma máquina tendremos que utilizar aplicaciones intermedias como Virtual Environment  para mantener la aplicación Flask ejecutándose como servicio, uWSGI como interfaz web entre servidores web y el servicio creado con Virtual Environment y NGINX como servidor web para controlar todas las peticiones entre la aplicación web y el servicio de transformaciones en Python.

0

Transformaciones con XSLT

XSLT es un lenguaje que permite transformar documentos XML en cualquier otro tipo documento. Este lenguaje busca dar solución al problema de expresar información estructurada de la forma más abstracta y reutilizable posible. Como veremos más adelante, este lenguaje resulta ideal para innumerables propósitos, ya que permite realizar transformaciones con XSLT de documentos de publicaciones con contenido básico en cualquier tipo de formato de presentación, como un micro sitio con contenido adaptativo a dispositivos móviles, un PDF, o un libro en formato EPUB listo para distribuirse a través de comercios de libros electrónicos.

Comenzaremos explicando los conceptos básicos relacionados con el lenguaje para posteriormente describir la forma en la que lo vamos a utilizar, así como el tipo de procesador XSLT que implementaremos.

Elementos en XSLT

XSLT se nutre de dos fuentes principales para funcionar: El documento XML a transformar y la plantilla XSLT de definición de las transformaciones. Estos dos documentos son utilizados por el procesador XSLT para generar un nuevo documento. La Figura 1 muestra la relación entre estos tres elementos.

Documentos en XSLT
Figura 1: Documentos en XSLT

Tanto el documento XML como la hoja de estilos deben ser documentos XML válidos y bien formados, lo que significa que una hoja de estilos se puede utilizar para transformar otra hoja de estilos.

Documentos XML

Un documento XML contiene elementos estructurados en forma de árbol definidos por medio de etiquetas rodeadas de corchetes angulares y barras (<,> y /). Para que un documento XML esté bien formado se deben seguir unas reglas básicas de estructuración de contenido. Estas reglas son las siguientes:

  • Todo documento XML debe estar contenido en un único elemento. Es decir, debe existir un elemento raíz que contenga a todos los demás elementos del documento.
  • Todos los elementos deben estar anidados. Si se abre un elemento dentro de otro elemento, el cierre del primer elemento debe aparecer antes del cierre del elemento en el que está contenido, es decir, un elemento no puede contener el cierre de un elemento si no contiene además su apertura.
  • Todos los valores de los atributos deben encontrarse entre comillas.
  • Las etiquetas XML son sensibles a mayúsculas y minúsculas. Esto es, las etiquetas, “<etiqueta></etiqueta>” y “<Etiqueta></Etiqueta>” son elementos distintos, por lo que “<etiqueta></Etiqueta>” sería un elemento mal formado.
  • Todos los elementos de cierre son obligatorios. Si creamos un elemento <img> que contiene otros elementos o contenido de texto, debe incluirse además su etiqueta de cierre </img>.
  • Si un elemento está vacío puede introducirse la marca de cierre en la etiqueta de apertura. Es decir, <etiqueta edad=”12”></etiqueta> y <etiqueta edad=”12”/> son elementos idénticos.

Para que además de bien formado, un documento XML sea válido, debe cumplir con esquemas predefinidos que establecen otras restricciones al contenido. Estos esquemas son las Definiciones de Tipo de Documento y los Esquemas XML, que vienen a ser metalenguajes utilizados para definir las características de un vocabulario XML (Doug Tidwell, XSLT.: O’Reilly, 2008). Con estos esquemas podemos definir, por ejemplo, las etiquetas válidas en un documento, así como los atributos que pueden contener. Estos esquemas XML se definen utilizando XML, por tanto, podríamos definir XML como un metalenguaje.

Por tanto, podemos tener un documento bien formado en XML, pero que no sea válido por no cumplir un esquema definido, pero no podemos tener un documento válido que no esté bien formado. En el Fragmento de Código 1 podemos ver un ejemplo de documento XML válido.

Cabe señalar que aunque existen similitudes entre XML y HTML, no son exactamente lo mismo. Como hemos señalado anteriormente, XML se trata de un metalenguaje que permite definir estructuras XML que definen documentos, mientras que HTML se trata de un lenguaje propiamente dicho, ya que está definido mediante un DTD o esquema XML. Esto provoca que en HTML podamos encontrar estructuras que, aunque se muestran perfectamente en un navegador, rompen las reglas de documentos bien formados definidas anteriormente. Véanse por ejemplo las etiquetas <br>.

En principio, un documento HTML debe “limpiarse” antes de ser utilizado en una transformación XSLT para asegurarse de que es un documento válido. Por suerte podemos realizar este tipo de tareas de una forma sencilla como veremos más adelante.

Hojas de Estilo XSLT

Las hojas de estilo escritas en lenguaje XSLT se basan en patrones o reglas que identifican los elementos del documento a transformar y muestran cómo deben presentarse esos elementos en el documento final.

El elemento raíz de una plantilla XSLT es <xsl:stylesheet> o <xsl:transform> que acostumbra a ir acompañado de los atributos xmlns que define el entorno de nombres de etiquetas que se van a utilizar, y el atributo version, que define la versión del esquema en el que vamos a basar las reglas de transformación.

Dentro de una hoja de estilos podemos encontrar otros elementos como:

  • <xsl:output>: Permite definir algunos parámetros del formato de salida, como la codificación, el lenguaje del documento a transformar, la indentación y demás atributos.
  • <xsl:variable>: Elemento que va a poder ser accesible en todo el documento de una forma más sencilla sin tener que definirlos cada vez que se vayan a utilizar.
  • <xsl:template>: elementos principales de una hoja de estilos ya que definen las transformaciones propiamente dichas. Poseen un atributo denominado match cuyo valor define la expresión que identificará a los elementos del documento XML que se deben transformar con este elemento.

Dentro de un elemento template se encuentra el contenido de la plantilla que define cómo convertir el contenido de la etiqueta XML del documento original. Para definir esta plantilla se pueden utilizar otros elementos de hoja de estilos que permiten realizar diferentes funciones. Entre esos elementos destacan los siguientes:

  • <xsl:value-of>: Establece que en el punto en el que se sitúa este elemento debe aparecer el valor del elemento que se corresponda con una expresión concreta que se establece con el atributo select.
  • <xsl:for-each>: Permite definir un bucle que se repetirá para todos los elementos que cumplan con una condición establecida en el atributo select del mismo.
  • <xsl:choose>: Utilizado en conjunción de los elementos <xsl:when> y <xsl:otherwise> se utiliza para crear estructuras condicionales que nos permitirá establecer un resultado u otro dependiendo de las reglas establecidas en el atributo test del elemento <xsl:when>

Partiendo del documento XML del Fragmento de Código 1, en el Fragmento de Código 2 podemos ver un ejemplo de hoja de estilos XSLT que transformará el documento XML en código HTML.

Como consecuencia de aplicar la transformación obtendremos el resultado en código HTML que podemos ver en el Fragmento de Código 3.

Esto es solo un pequeño ejemplo de las posibilidades que nos brindan las transformaciones en XSLT ya que existen muchos más elementos que se pueden utilizar dentro de una hoja de estilos XSLT para definir transformaciones.

XPath

Como hemos podido observar hasta ahora, tanto los documentos como las plantillas son documentos XML. Sin embargo, para poder navegar y encontrar elementos XML concretos dentro de un documento debemos utilizar otro lenguaje que nos permita identificar elementos que cumplan ciertos criterios de forma sencilla. En XSLT, el lenguaje que permite realizar estas acciones se denomina XPath y lo hemos podido ver en ejemplos anteriores en los atributos match, test, y select de los elementos <xsl:template>, <xsl:when> y <xsl:value-of> respectivamente.

En XPath todo objeto XML es tratado como un nodo. A su vez, se distinguen siete tipos de nodos:

  • Elemento Raíz o Documento: Elemento principal de un documento XML dentro del cual se definen el resto de elementos.
  • Elementos: Objetos XML encerrados entre corchetes angulares. Por ejemplo: <autor></autor>.
  • Atributo: Propiedad de un elemento cuyo valor se encuentra entrecomillado: Por ejemplo: edad=”23”.
  • Texto: Cualquier cadena de texto que se encuentre entrecomillada en un atributo, o dentro de un elemento. Tomando como ejemplo el elemento <autor>Miguel</autor>, el nodo texto sería el valor “Miguel”.
  • Comentario: Cadena de texto situada dentro de un elemento entre las cadenas de caracteres “<!–» y “–>”. Por ejemplo: <!– Esto es un comentario –>.

Para relacionar nodos, XPath define cinco parentescos entre ellos:

  • Padre: Nodo en el que está contenido un nodo concreto. Todo elemento o atributo posee un nodo padre.
  • Hijo: Nodos contenidos en un nodo concreto.
  • Hermano: Nodos que poseen el mismo padre.
  • Ancestro: Nodo padre de un nodo, así como el nodo padre del nodo padre, etc., es decir, todos los elementos padre desde un nodo concreto hasta el raíz.
  • Descendientes: Nodos hijo de un nodo, así como los hijos de cada nodo hijo, etc., es decir, todos los elementos hijo desde un nodo concreto.

XPath define una sintaxis para definir partes en un documento XML. Utiliza expresiones parecidas a las rutas para navegar entre los elementos y contiene una biblioteca de funciones que se utilizan para distinguir elementos.

Las expresiones más comunes en XPath podrían resumirse en las siguientes:

  • nombrenodo: Selecciona todos los nodos con el nombre “nombrenodo”.
  • /: Selecciona nodos desde el nodo raíz.
  • //: Selecciona nodos en el documento desde el nodo actual que cumplan con la selección independientemente de dónde se encuentren.
  • .: Selecciona el nodo actual.
  • ..: Selecciona el nodo padre.
  • @: Selecciona atributos.

Con estas expresiones se pueden construir rutas de navegación como las siguientes:

  • documento: Selecciona todos los nodos con el nombre “documento”.
  • /Coleccion: Selecciona el nodo raíz “Coleccion”.
  • Coleccion/documento: Selecciona todos los elementos “documento” hijos de “Colección”
  • //documento: Selecciona todos los elementos “documento” que se encuentren en el documento sin importar su lugar.
  • Coleccion//documento: Selecciona todos los elementos “documento” descendientes del elemento “Coleccion”.
  • //@edad: Selecciona todos los atributos “edad” del documento.

Además de seleccionar nodos en relación a su posición en el documento, podemos seleccionar nodos concretos dependiendo de ciertas características. Para encontrar nodos que contentan ciertos valores o cumplan ciertas propiedades utilizaremos los denominados “predicados”. Los predicados se incluyen en las expresiones XPath entre corchetes. A continuación podemos ver algunos ejemplos:

  • /Coleccion/documento[1]: Selecciona el primer elemento documento hijo del elemento raíz Coleccion.
  • /Coleccion/documento[last()-1]: Selecciona el penúltimo elemento documento hijo del elemento raíz Coleccion.
  • /Colección/documento[fecha > 2012]/titulo: Selecciona los elementos titulo hijos de los elementos documento, a su vez hijos del elemento raíz Coleccion cuyo valor del elemento fecha sea superior a 2012.

Se pueden anidar expresiones haciendo uso del operador “|”. Por ejemplo:

  • //titulo | //autor : Selecciona todos los nodos titulo Y autor del documento.

Por otro lado, se pueden utilizar comodines en las expresiones XPath, lo que nos permite seleccionar nodos dependiendo de expresiones múltiples. Los comodines más habituales son:

  • *: Selecciona cualquier nodo elemento.
  • @*: Selecciona cualquier nodo atributo.
  • node(): Selecciona cualquier nodo de cualquier tipo.

Finalmente, cabe destacar que tanto XSLT como XPath cuentan con una biblioteca de funciones que les permite seleccionar elementos de una forma mucho más eficiente. Podemos encontrar un listado de las mismas en la propia especificación.

Conclusión

En definitiva, XSLT es un lenguaje extremadamente potente que nos permite realizar transformaciones de una forma sencilla y rápida. Aunque la curva de aprendizaje pueda ser un poco complicada, una vez se han realizado unas cuantas transformaciones y se han estudiado correctamente las capacidades de selección de nodos con XPath las posibilidades se abren a cada paso.

0

Clean HPub Specification 1.0

En el siguiente artículo se detallan las características que debe poseer un paquete CHPub (Clean HPub) para ser transformado en cualquier sistema de publicación. Basado en la especificación de HPub de Alessandro Morandi para Baker Framework pretende simplificar el mismo y delegar la tarea del diseño y la forma de presentación en el medio de transformación.

Especificación

  1. Una publicación CHPub es un conjunto de ficheros empaquetados en un fichero ZIP con el formato: [NOMBRE].zip
  2. Una publicación está formada por diferentes apartados. Cada apartado constará de un fichero HTML nombrado con el formato [NOMBRE]_A[XX].html siendo NOMBRE el nombre de la publicación, y XX un número entre 00 y 99.
  3. Los recursos adicionales de cada apartado como imágenes, vídeos, etc. serán localizados en un directorio de nombre [NOMBRE]_A[XX], y referenciados en su correspondiente fichero de apartado de forma relativa.
    • Ej.: <img src="CHPub_A02/img/img01.jpg" alt="Lorem"/>
  4. Incluye un fichero denominado «book.json» con metadatos sobre la publicación como se describe en el apartado book.json.
  5. El código HTML que se utilizará en los ficheros deberá ser lo más sencillo posible, evitando utilizar CSS y limitando el uso de etiquetas más allá de las que se especifican en el correspondiente apartado etiquetas.
  6. Se permite el uso de etiquetas personalizadas relacionadas con extensiones definidas en el apartado extensiones.
  7. Incluyendo una imagen en la raíz de la publicación con el nombre cover.jpg se podrá utilizar en las portadas de las publicaciones que se generarán. Se recomienda una resolución de 1920×1080.

Estructura

book.json

El fichero book.json se situará en la raíz de la publicación y deberá contener un objeto JSON bien formado. Ejemplo de fichero book.json:

Parámetros

  • title (string): Título de la publicación.
  • author (array): Autor/es de la publicación. Persona/s generadoras de los contenidos independientemente del formato. Un string con el nombre de cada autor diferente.
  • creator (array): Creador/es de la publicación. Persona/s creadoras de la publicación en CHPub. Un string con el nombre de cada creador diferente.
  • editor (string): Nombre del editor de la publicación.
  • date (string): Fecha de la publicación en formato YYYY-MM-DD.
  • url (string): URL de la publicación o de la editorial.
  • description (string): Descripción de la publicación.
  • keywords (string): Palabras clave relacionadas con la publicación.
  • contents (array): Lista de todos los ficheros de apartados de la publicación en el orden de visualización.

Etiquetas

A diferencia de la especificación original de HPub, en Clean HPub se pretenden crear publicaciones con el menor diseño posible, centradas en el contenido. De esta forma el diseño dependerá exclusivamente del sistema de transformación que reciba la publicación como entrada y genere una publicación en otro formato, ya sea PDF, EPub, etc. La regla es simple: Se permite cualquier etiqueta del estándar HTML4 siempre y cuando no se utilicen el atributo style. Más adelante, en el apartado extensiones, se detallarán etiquetas adicionales relacionadas con los componentes extra que se podrán utilizar.

Relación de etiquetas HTML válidas

Para limitar el diseño se especifican a continuación las etiquetas HTML que se permitirán por defecto en lo ficheros HTML de cada apartado para una publicación de libro normal.

  • html: Etiqueta contenedora de las etiquetas head y body.
  • head: En ella se incluirán algunos metadatos permitidos para cada apartado. Por el momento las etiquetas permitidas son:
    • title: Título del apartado.
  • body: Etiqueta contenedora del resto de etiquetas del apartado.
  • h1: Título del apartado. Solo debe existir uno.
  • hX: Títulos de subapartados siendo X el nivel de ordenación. Se debe respetar la estructura de niveles de subarpartados:
    • No se debe introducir un h3 si antes no se ha introducido un h2.
    • Todo h3 se considera subapartado del h2 inmediatamente anterior, del mismo modo que un h4 lo hará para un h3, y así sucesivamente.
  • p: Párrafos de contenido.
  • blockquote: Se utiliza para hacer referencia a una cita externa.
  • strong: Texto en negrita.
  • u: Texto subrayado.
  • i: Texto en cursiva.
  • a: Hiperenlace. Los siguientes atributos son obligatorios:
    • href: Ruta del enlace.
    • title: Descripción del enlace.
  • ul: Lista no numerada. Para cada elemento de la lista se utilizará la etiqueta li.
  • ol: Lista numerada. Para cada elemento de la lista se utilizará la etiqueta li.
  • img: Imágenes. Los siguientes atributos son obligatorios:
    • src: Ruta relativa a la imagen. Las imágenes deberán encontrarse en la carpeta del apartado.
    • alt: Descripción de la imagen.

Extensiones

Además de las etiquetas HTML básicas descritas anteriormente, y para aumentar la versatilidad de las publicaciones, se han creado etiquetas específicas para diferentes tipos de material. Algunas de ellas están relacionadas con extensiones externas HTML5 que cambiarán dependiendo de la transformación a realizar.

Etiquetas

  • gallery: Esta etiqueta se utilizará para englobar a un conjunto de etiquetas img y mostrarlas como una galería de imágenes.

  • tooltip: Permite mostrar un pequeño tooltip de ayuda sobre un texto concreto. Útil para notas de autor o pequeñas aclaraciones.

  • video: Incluye un vídeo en la publicación. El vídeo podrá ser un fichero en la carpeta del apartado o un identificador de vídeo externo como Vimeo o Youtube. En los siguientes ejemplos los comentarios no son necesarios, solo se añaden para que se identifique el identificador de cada vídeo externo:

  • videogallery: Genera una galería con los vídeos que se introduzcan entre la etiqueta de apertura y cierre:

  • audio: Etiqueta de audio que hará referencia a un fichero en la carpeta del apartado.

  • columns: Estructura el contenido en dos o más columnas. Cada columna se representa con la etiqueta column.

Ejemplo de Apartado

Meta

0

MEAN: AngularJS

Para finalizar con las entradas relacionadas con el stack MEAN, me dispongo a describir las características de AngularJS. Antes de comenzar me gustaría señalar que toda la información recogida en este artículo se refiere a la versión 1.x de AngularJS, nada que ver con Angular 2 y sus versiones posteriores. Sin embargo, debido a la gran cantidad de aplicaciones desarrolladas con la versión 1.x, no parece que vaya a desaparecer en un futuro cercano, ya que su desarrollo continua activo.

Comenzaré explicando lo que es un SPA (Single Page Application) y para qué sirve, para seguidamente hablar por encima de MVC (Modelo-Vista-Controlador) y finalmente detallar los pormenores de AngularJS con algunos ejemplos e imágenes.

Single Page Applications (SPA)

Uno de los principales objetivos en el desarrollo de aplicaciones web es conseguir la mejor experiencia de usuario posible. Esto se logra, principalmente, disminuyendo los tiempos de espera entre vistas, intentando conseguir la mayor semejanza con las aplicaciones nativas.

En las aplicaciones web clásicas la lógica de negocio se realiza en su mayor parte en el servidor, siendo éste el encargado de comunicarse con la lógica de datos y decidir qué mostrar y qué no dependiendo en gran medida de la URL que se quiere consultar, esto es, de la vista que se quiere mostrar. Para el navegador cada URL es independiente, por lo que, aunque en cada vista se carguen los mismos ficheros de estilos y/o plantillas, éstos tienen que volver a ser procesados. Esto crea un problema de latencia que perjudica la experiencia de usuario.

Otro problema que presentan las aplicaciones web clásicas es que su mantenimiento resulta muy costoso debido a la dificultad existente a la hora de transmitir información entre una vista y otra, que implica utilizar variables de sesión cuya especificación ambas vistas tienen que compartir o cualquier otra solución que el desarrollador crea mejor.

Un SPA (Single-Page Application) o aplicación de página única es una aplicación web que cabe en una sola página con el propósito de dar una experiencia más fluida al usuario. De esta forma, una vez el usuario entra por primera vez, toda la lógica de la interfaz de usuario se descarga en el navegador y así puede interaccionar con la misma y navegar por sus contenidos de forma sencilla sin tener que cambiar de página, ahorrándose así los tiempos de carga.

En un SPA las vistas pasan a denominarse estados, contando éstos con vistas o plantillas independientes que se mostrarán dependiendo del estado activo. Los cambios de estado se producen cuando el usuario realiza cualquier interacción con la aplicación web, modificando las zonas pertinentes de la aplicación web para mostrar las vistas y elementos del nuevo estado.

En una aplicación web de este tipo podemos encontrar elementos comunes y áreas de intercambio de vistas. Los elementos comunes son aquellos que permanecerán estáticos en la mayoría de los estados, mientras que en las áreas de intercambio de vistas se producirán los cambios de estado, mostrando la información necesaria que se corresponda con cada estado diferente.

Si la aplicación requiriera de una comunicación con el servidor externo, ya sea para comunicarse con la base de datos, o para realizar alguna tarea que requiera de una mayor velocidad de proceso, lo haría de forma asíncrona utilizando AJAX que permite realizar peticiones HTTP a una API REST que se encargue de realizar las tareas pertinentes en el servidor y devuelva al cliente información relevante para que el navegador informe al usuario de la situación de cada petición.

En la Figura 1 podemos comparar los ciclos de vida de una aplicación web clásica y un SPA. Mientras que las tradicionales requieren de recargas para cada vista diferente siendo el servidor el encargado de suministrar el código HTML para cada petición distinta, en un SPA se realiza una petición inicial al servidor por la que el servidor devuelve un conjunto de recursos que el navegador utilizará para mostrar al usuario la aplicación web. Mientras que la aplicación no requiera de información adicional del servidor el navegador no necesitará realizar ningún tipo de petición adicional, pero en el caso de que así fuera, la petición se realizaría de forma asíncrona y el servidor no devolvería una vista completa, si no que solo devolvería información estructurada relacionada con la petición realizada, como, por ejemplo, un listado de productos.

Figura 1: Ciclos de Vida de una Web tradicional y un SPA

Existen, sin embargo, algunas desventajas en el uso de SPA que requieren de una atención y trabajo especial. Entre ellas, la más importante debido a la propia definición de un SPA, es la dificultad para optimizar su rastreo por motores de búsqueda, ya que los rastreadores no ejecutan código Javascript, que es necesario en los cambios de estado de un SPA. Por este motivo no es recomendable utilizar SPAs en un entorno en el que el SEO sea un requisito, ya que la optimización para buscadores en este caso no es trivial, al menos por ahora.

Otra desventaja es el obstáculo que se encuentra el usuario al hacer uso de los botones de navegación del navegador, esto es, ir a la página anterior o a la siguiente. Al tratarse de una página única, al intentar realizar estas acciones las páginas anteriores o siguientes se corresponden con las páginas que se accedieron antes o después de ingresar en la SPA. Para esto existen soluciones bastante sencillas haciendo uso de los métodos pushState y replaceState de la especificación de HTML5

Teóricamente, se puede diseñar un SPA utilizando tan solo la especificación de HTML 4, sin embargo, en HTML 5 encontramos varias APIs que facilitan en gran medida el desarrollo de las mismas. Por otro lado, existen muchas herramientas que nos permiten crear SPAs de forma efectiva, entre ellas se encuentran diferentes frameworks como Ember.js, Backbone.js, ReactAngularJS y Angular. Todos ellos cumplen la arquitectura MVC o derivados.

MVC

El modelo–vista–controlador (MVC) es un patrón de arquitectura de software que separa los datos y la lógica de negocio de una aplicación de la interfaz de usuario y el módulo encargado de gestionar los eventos y las comunicaciones. Para ello MVC propone la construcción de tres componentes distintos que son el modelo, la vista y el controlador, es decir, por un lado define componentes para la representación de la información, y por otro lado para la interacción del usuario. Este patrón de arquitectura de software se basa en las ideas de reutilización de código y la separación de conceptos, características que buscan facilitar la tarea de desarrollo de aplicaciones y su posterior mantenimiento. (Wikipedia)

Modelo

Se encarga de proveer datos. Suele ser una API. También puede ser un objeto JavaScript o cualquier otra cosa. Es la representación de la información que va a manejar el sistema y se encarga por tanto de controlar los accesos a la misma y la forma en la que se devuelven. Envía a la ‘vista’ aquella parte de la información que en cada momento se le solicita para que sea mostrada. Las peticiones de acceso o manipulación de información llegan al ‘modelo’ a través del ‘controlador’.

Vista

Representa cómo se muestra el modelo al usuario. Es dónde el usuario puede interaccionar con la aplicación.

Controlador

Se sitúa en el centro del sistema y se encarga de decidir cómo procesar los datos del usuario, de dónde obtener los datos, qué hacer con ellos, etc. Invoca peticiones al ‘modelo’ cuando se hace alguna solicitud sobre la información (por ejemplo, editar un documento o un registro en una base de datos). También puede enviar comandos a su ‘vista’ asociada si se solicita un cambio en la forma en que se presenta el ‘modelo’ (por ejemplo, desplazamiento o scroll por un documento o por los diferentes registros de una base de datos), por tanto se podría decir que el ‘controlador’ hace de intermediario entre la ‘vista’ y el ‘modelo’.

Conclusión

Utilizar este patrón nos proporciona, entre otras, las siguientes ventajas:

  • Permite cambiar la fuente de datos para una vista sin necesidad de cambiar el código de éstas vistas.
  • Posibilita cambiar el diseño de una vista, por ejemplo, una nueva librería CSS, sin necesidad de cambiar la lógica de interfaz de usuario.

La mayoría de los frameworks MVC que nos permiten crear SPAs deben cumplir las siguientes características:

  • Enlace de datos de doble vía: Si cambiamos algo en la vista, el modelo cambia de forma automática. Del mismo modo, si el modelo cambia, la vista a su vez se actualiza de forma inmediata.
  • Inyección de dependencias: Técnica en la que el propio framework se encarga de cargar los módulos necesarios en las diferentes estructuras del código y en el momento en el que vaya a usarse. Por ejemplo, si en un módulo necesitamos la librería NetworkGraphVisualization, y esta a su vez necesita D3Visualization y SliderWidget, las cuales, además, necesitan jQuery, el framework de inyección de dependencias se encargará de todo este trabajo por nosotros. Esto nos permite cumplir el patrón de separación de conceptos.
  • Enrutamiento: Capacidad de indexar una vista en la aplicación con una URL específica y poder cambiar la URL cuando la aplicación cambie de estado.
  • Plantillas: Permite definir una estructura HTML en la que se introducen espacios con marcas que se podrán sustituir con la información necesaria para cada situación por medio de simples reemplazos de texto. Las plantillas pueden ser sencillas cadenas de texto con códigos de reemplazo o ficheros HTML preparados.

AngularJS

AngularJS es un framework de JavaScript de código abierto mantenido por Google. Su objetivo es aumentar las aplicaciones basadas en navegador con capacidad de MVC, en un esfuerzo para hacer que el desarrollo y las pruebas sean más fáciles.

La comunidad de desarrolladores generando consultas y soluciones en la red es muy importante, habiendo creado un ecosistema lo suficientemente potente como para permitir resolver cualquier complicación en el menor tiempo posible encontrando siempre soluciones óptimas.

Una de las desventajas de AngularJS es que la curva de aprendizaje es un poco abrupta. La mayor parte de los desarrolladores coinciden en que, a pesar de que el entorno les encanta, cuando encuentran nuevas dificultades les es complicado entender los conceptos necesarios para continuar. Sin embargo, una vez entendidos los conceptos y obtenidos los conocimientos necesarios para avanzar, lo recién aprendido hace que el entusiasmo vuelva a crecer. En la Figura 2 podemos encontrar un ejemplo hilarante de los sentimientos con los que se encuentra todo desarrollador de Angular.

Figura 2: Reacciones en el aprendizaje de Angular JS

Está basado en la creación de etiquetas HTML personalizadas así como atributos que son interpretados por el framework y que ayudan a reescribir las partes necesarias de la aplicación. Además de las etiquetas personalizadas crea diferentes conceptos con la idea de estructurar correctamente cada aplicación y de esa manera hacerla más mantenible. Entre los componentes de AngularJS más importantes encontramos los siguientes:

  • Vistas: Conforman la interfaz de usuario y definen como se muestra la información al usuario.
  • Controladores: Trabajan con toda la lógica detrás de la interfaz de usuario y hacen uso de los Servicios para obtener información.
  • Servicios: Se encargan de la comunicación con el servidor y mantiene ordenado el código con funcionalidades relacionadas.
  • Directivas: Facilitan la tarea de crear etiquetas HTML personalizadas extendiendo su funcionalidad creando nuevos elementos, atributos y comportamientos.

La estructura de una aplicación AngularJS es bastante legible, esto es, la mayor parte de sus métodos están nombrados de una forma muy descriptiva, de tal forma que con solo leerlos es fácil intuir qué es lo que hacen.

Módulos

Cómo mínimo, toda aplicación en AngularJS necesita que sea definido un módulo principal. Un módulo es un contenedor de partes de una aplicación. En la creación de éstos se pueden incluir dependencias como otros módulos o librerías, por lo que la modularización resulta bastante simple. Dentro de un módulo se deben definir el resto de componentes, es decir, todo elemento en AngularJS pertenece a un módulo. En el Fragmento de Código 1 podemos ver el código necesario para inicializar un módulo llamado “myApp” en una aplicación. Si quisiéramos incluir alguna dependencia, cómo puede ser otro módulo o librería, lo haríamos introduciéndolos como un array de cadenas en el segundo parámetro. Como podemos ver en el ejemplo por defecto se introduce un array vacío.

Para referenciar al módulo principal en el código HTML utilizaremos el atributo ng-app que introduciremos en la etiqueta dónde queramos inicializar la aplicación. Por lo general este atributo se introduce en la etiqueta html.

Controladores

Los controladores se encargan de manipular la información en las aplicaciones Angular. Son simples objetos Javascript con métodos y propiedades. Se definen siempre dentro de un módulo.

Entre los objetos más importantes que puede manejar un controlador se encuentra la variable $scope. Esta variable es el enlace entre el código HTML (vista) y el código JavaScript (controlador). En ella se pueden introducir métodos y variables que podrán ser utilizados y modificados desde ambas partes de la aplicación, de tal forma que si el controlador cambiara este objeto, la vista se vería directamente afectada y cambiaría de forma inmediata cualquier información relacionada que se estuviera mostrando al usuario en ese momento. En el Fragmento de Código 2 podemos ver un ejemplo de definición de controlador que hace uso de la variable $scope y le asigna un valor a una variable del mismo.

Para indicar en el código HTML qué etiqueta padre queremos que sea utilizada y enlazada con un controlador concreto haremos uso del atributo ng-controller.

Directivas

Angular JS permite extender el lenguaje HTML con nuevos atributos y etiquetas denominados directivas. Angular provee muchas directivas predefinidas que añaden funcionalidad a las aplicaciones de forma nativa, pero también nos permite crear directivas personalizadas. Todas las directivas predefinidas comienzan con el prefijo “ng“, por lo que no conviene utilizar ese mismo prefijo para crear directivas personalizadas. Entre las directivas predefinidas más utilizadas se encuentran las siguientes:

  • ng-app: Inicializa una aplicación en Angular JS. El valor que se le da hace referencia al nombre del módulo principal.
  • ng-controller: Define el controlador a utilizar dentro de la etiqueta HTML en la que se ha asignado.
  • ng-model: Enlaza controles HTML (input, select, textarea) con los datos de la aplicación. Por lo general los valores que toma este atributo suelen ser los nombres de las variables definidos en la variable $scope en el controlador.
  • ng-repeat: Permite repetir una porción de código HTML en relación a una variable iterable inicializada en un atributo ng-init o en la variable $scope del controlador.

En el Fragmento de Código 3 podemos ver un ejemplo de código HTML en el que se han utilizado todas las directivas anteriormente mencionadas.

Como podemos observar definimos la aplicación en la etiqueta html, sin embargo esto no es necesario, ya que podría inicializarse en cualquier etiqueta. Dentro de la etiqueta head encontramos la referencia a la librería de Angular JS mínima necesaria para que todo funcione, y al fichero app.js de nuestra aplicación que es dónde se ha definido el módulo principal que denominamos “myApp” y el controlador “myController” que vimos en ejemplos anteriores, es decir, app.js contiene únicamente el Fragmento de Código 2 y el Fragmento de Código 3.

Definimos el controlador en la etiqueta body, pero del mismo modo que sucedía con el atributo ng-app, podemos definirlo en cualquier otra etiqueta siempre y cuando la etiqueta del controlador sea la misma, o hija de la etiqueta en la que se define el atributo ng-app.

Las expresiones de Angular se introducen en el código HTML entre llaves dobles. De esta forma, la expresión {{name}} será sustituida por la variable name perteneciente a $scope, o a cualquier expresión que sea capaz de relacionarse. Las expresiones en Angular son muy parecidas a las expresiones de JavaScript, por tanto, podríamos utilizar expresiones del tipo {{ 5 + 5 }} que mostraría el resultado de la suma en el navegador, o concatenaciones como {{ nombre + “ “ + apellidos }} que mostraría en la página la concatenación de las variables nombre y apellidos.

El resultado de visualizar el Fragmento de Código 3 en un navegador HTML se puede observar en la Captura 2.

Captura 2: Ejecución de Aplicación Angular

Todas las expresiones de Angular han sido sustituidas por sus valores. Con el uso de ng-repeat hemos conseguido que se repita una etiqueta li tantas veces como elementos existen en el array asociado. Por otro lado, hemos enlazado el modelo de datos “name” a la etiqueta de tipo input, por lo que si modificáramos el contenido de la caja, éste cambiaría de forma automática como podemos observar en la Captura 3. Del mismo modo, si en alguna parte del código de la aplicación se modificaran las variables name o languages, los valores que se muestran en el navegador cambiarían de forma automática.

Captura 3: Modificación de valores del modelo asociado

Directivas Personalizadas

Otra de las grandes ventajas de Angular es que nos permite crear nuestras propias directivas asociadas a etiquetas HTML personalizadas. Haciendo uso de este tipo de directivas podemos reutilizar código HTML de una forma muy eficiente, además de poder personalizar el comportamiento y las características de cada una de estas directivas de forma sencilla. Esto nos permite estructurar una aplicación web en distintos componentes con su propio código HTML con sus propios controladores y servicios.

La creación de una directiva propia se hace mediante la función directive asociada a los módulos Angular. Es importante tener en cuenta que los nombres de las directivas en el código HTML y en el JavaScript no son exactamente igual. Si la etiqueta HTML asociada a la directiva se llamara “my-directive” el nombre que habrá que darle a la directiva en su creación en el código de la aplicación será haciendo uso del estilo de escritura “lowerCamelCase”, dónde la primera letra de cada palabra se escribe en mayúsculas a expepción de la inicial, siendo así el nombre de la directiva myDirective. En Fragmento de Código 4 podemos ver un ejemplo de creación de directiva en la que hemos incluido parte del código del ejemplo anterior visto en el Fragmento de Código 3.

Con esta simple directiva nuestro código HTML quedaría como en el Fragmento de Código 5, dando como resultado exactamente el mismo que pudimos observar en la Captura 2 y la Captura 3.

Una directiva no tiene porqué utilizarse solo como una etiqueta. Esta característica nos otorga gran versatilidad a la hora de utilizarlas. Se puede invocar una directiva de las siguientes formas, obteniéndose el mismo resultado en todas ellas:

  • Como un elemento o etiqueta HTML:
    • <my-directive></my-directive>
  • Como un atributo:
    • <div my-directive></div>
  • Como una clase:
    • <div class=”my-directive”></div>
  • Como un comentario:
    • <!– directive: my-directive –>

Sin embargo, una directiva puede restringirse para que solo pueda ser utilizada de una de las formas anteriormente especificadas. Para ello se hace uso de la propiedad restrict, que toma como valores alguno de los siguientes, o combinaciones de ellos:

  • E: solo como elemento o etiqueta
  • A: solo como atributo
  • C: solo como clase
  • M: solo como comentario

Como mencionábamos anteriormente una directiva puede contener su propio controlador. Para ello solo habría que definir la función que hará de controlador en el parámetro link. Existe otro parámetro de características similares pero que se ejecutará en otro momento de la carga de la aplicación, que es el parámetro controller. Para saber en qué momento utilizar una y otra hay que tener en cuenta la siguiente diferenciación:

  • link: Se ejecuta después de la compilación
  • controller: Se ejecuta antes de la compilación

Otra propiedad interesante dentro de las directivas es templateUrl, que nos permite especificar una ruta a un fichero html en el que se encontrará el código html de la directiva. De esta forma nos ahorraremos utilizar cadenas de texto con código html en el propio código de la aplicación.

En el Fragmento de Código 6 podemos observar una directiva completa en la que se hace uso de todos los parámetros anteriormente mencionados.

Cómo podemos observar el controlador recibe los siguientes parámetros:

  • $scope: Misma variable general utilizada en el controlador principal. De no existir controlador principal $scope estaría vacía, pero podría utilizarse dentro de la directiva de forma independiente.
  • $element: Esta variable contiene todas las propiedades y estados del objeto HTML que invoca a la directiva, de tal forma que podemos acceder desde este elemento a otros atributos introducidos en el mismo como clases o estilos y modificarlos si hiciera falta.

Por otro lado, la declaración del valor del parámetro link recibe los siguientes atributos equivalentes:

  • scope: Se refiere a la misma variable scope que recibe el parámetro controlador.
  • element: Al igual que sucede con scope, element es el mismo objeto que recibe el parámetro $element del controlador.
  • attr: Relación de atributos del objeto que invoca la directiva.

En el fichero templates/mydirective.html podemos encontrar el código HTML que anteriormente utilizábamos como cadena de texto en el Fragmento de Código 3 e inicialmente en el Fragmento de Código 3.

En la Captura 4 podemos observar el resultado de ejecutar la aplicación. En ella se puede apreciar el comportamiento de los parámetros link y controller, ejecutándose controller antes que link.

Captura 4: Resultado de utilización de parámetros link y controller

Es importante señalar que a los parámetros link y controller no solo los diferencia el orden de ejecución. Cuando el parámetro controller es ejecutado la aplicación no ha terminado de cargarse todavía, es por eso que este parámetro no recibe el parámetro de atributos que si recibe link, ya que link se ejecuta cuando la página se ha cargado completamente y se puede acceder a todos los objetos y propiedades de los mismos.

Existen otros parámetros y usos más avanzados para las directivas que se salen el contexto de una introducción. Para más información acerca de cómo utilizar las directivas remitimos al lector a la Guía de Desarrollador de AngularJS.

Servicios

En Angular los servicios son funciones u objetos disponibles en todo el entorno de una aplicación, destinados a realizar tareas concretas y a proporcionar información específica a los controladores o a cualquier otro elemento que haga uso de ellos. Angular contiene más de 30 servicios predefinidos. En muchas ocasiones estos servicios predefinidos proporcionan información que podría obtenerse de otras formas dentro de lo que es la ejecución de código JavaScript, como obtener la url de la página actual, o el título del documento. Sin embargo, y puesto que Angular está constantemente revisando el estado de la aplicación, es recomendable utilizar estos servicios en lugar de los métodos estándar, para de esa manera se mejore la comunicación entre los diferentes módulos y objetos de nuestra aplicación.

Los servicios se utilizan para compartir funcionalidades en una aplicación, de tal forma que no haya que repetir código. Un ejemplo podría ser crear un servicio de acceso a una tabla concreta de una base de datos con todos los métodos de consulta y modificación. Cualquier parte de la aplicación que necesitara consultar esa tabla solo tendría que utilizar el servicio sin necesidad de crear sus propios métodos de acceso a la base de datos o al servidor pertinente.

Un aspecto importante de los servicios es que Angular solo los inicializa cuando van a ser utilizados, y una vez son cargados en memoria, si diferentes objetos hacen uso de un mismo servicio, Angular no crea diferentes versiones del servicio para cada componente, si no que crea una sola instancia que comparten todos los componentes de forma segura.

Para que un controlador pueda hacer uso de un servicio, éste debe de ser pasado como parámetro en la definición del controlador. Entre los servicios predefinidos destacan los siguientes:

$location

Proporciona información acerca de la URL que aparece en la barra de dirección del navegador, haciéndola disponible para toda la aplicación. Además proporciona métodos para modificar la URL actual y para navegar entre los distintos enlaces de una aplicación. Incluye métodos de respuesta ante cambios de URL, de tal manera que podemos procesar cualquier URL antes de que sea mostrada. En general, cambios en la url de la barra de direcciones se reflejan inmediatamente en el servicio $location y viceversa.

En él podemos observar la declaración de un controlador que hace uso del servicio $location para obtener el puerto en el que se está ejecutando la aplicación, y almacenarlo en la variable de sesión puerto.

$http

Este servicio es uno de los más utilizados en una aplicación Angular, ya que permite realizar peticiones de tipo http a un servidor y obtener información de respuesta. Esto se utiliza, entre otras cosas, para cargar objetos de base de datos en una variable y poder mostrarla correctamente en la página de la aplicación.

Los métodos que implementa se corresponden con las peticiones http más utilizadas que son GET, POST, PUT y DELETE entre otras, lo que nos permite consumir APIs de tipo REST sin ningún problema y de una forma bastante sencilla.

Incorpora métodos para el tratamiento de la información recibida de forma asíncrona. Esto permite que la aplicación no tenga que quedarse esperando a que el servidor devuelva los datos para poder manejarlos, si no que una vez realizada la petición, la aplicación continúa su ejecución y, en cuanto llega la información solicitada, se ejecuta el código que se haya introducido como método de respuesta.

En el Fragmento de Código 8 podemos ver un ejemplo de cómo cargar la variable languages de la aplicación de forma externa haciendo uso del servicio $http. El resultado será equivalente al obtenido anteriormente en ejemplos anteriores que pudimos observar en la Captura 3.

En este caso la información de la variable languages se obtiene de un objeto de tipo JSON incluido en el fichero languages.json. Dentro del objeto “response” se encuentra toda la información relacionada con la respuesta obtenida en la petición GET. Entre las propiedades de este objeto se encuentra el objeto “data” que incluye toda la información devuelta por la petición. Al tratarse de un objeto JSON, Angular convierte automáticamente el contenido a un objeto JavaScript, en este caso un array, y lo trata como tal. El contenido del fichero languages.json se puede observar en el Fragmento de Código 9.

Con el uso de este servicio podríamos implementar fácilmente toda la capa de lógica de datos de una aplicación web habitual, dejando al servidor la tarea de obtener los datos y convertirlos a una cadena JSON.

$timeout e $interval

Estos servicios nos permiten realizar tareas concretas pasado un determinado periodo de tiempo, o de forma repetitiva en intervalos. El comportamiento de éstos es similar a las funciones JavaScript “window.setTimeout” y “window.setInterval”. En el Fragmento de Código 10 podemos observar un ejemplo de ambos servicios.

En este ejemplo la variable “name” pasaría a ser “Antonio” al pasar 2 segundos, y por otro lado cada 5 segundos se actualizaría la misma variable con el valor de la hora actual.

Servicios Personalizados

Al igual que sucedía con las directivas, en Angular también se pueden crear servicios personalizados. En este caso un servicio personalizado se correspondería con un objeto JavaScript con variables y funciones propias que se podrán utilizar de forma externa desde un controlador que reciba el servicio como parámetro.

Los servicios personalizados deben pertenecer a un módulo de la aplicación. La forma de crear un servicio es similar a las anteriormente vistas. En este caso utilizaremos el método service para definir el servicio. Este método recibe como primer parámetro el nombre del servicio por el que se le conocerá en toda la aplicación, y como segundo parámetro una función u objeto JavaScript que definirá el servicio. En el Fragmento de Código 11 podemos observar un ejemplo de servicio que realiza la tarea de obtener los valores de la variable languages utilizada anteriormente, así como un ejemplo de uso de este servicio en el controlador principal que venimos utilizando, obteniéndose como siempre el mismo resultado.

Un servicio puede hacer uso de otros servicios sin necesidad de que éstos últimos sean declarados en el controlador. De esta forma se consigue independizar totalmente las diferentes capas de la aplicación. Esto nos permitiría, cómo podemos ver en el Fragmento de Código 12, obtener los datos de forma remota con el servicio $http desde el servicio Languages personalizado.

Sin embargo, hay que tener en cuenta que mientras que la petición en el controlador se hace de forma síncrona, la obtención de datos en el servicio con $http se hace de forma asíncrona. De esta forma, aunque el código anterior no provoque ninguna excepción, el resultado será que al realizar la petición al servicio, éste devolverá una variable no definida, y cuando se haya realizado la petición y obtenido los datos, ese “return” no será recibido por nada.

Para solucionar este problema, Angular provee otro servicio que implementa un sistema de promesas, que vienen a ser métodos para ejecutar funciones de forma asíncrona y poder utilizar los resultados de forma correcta cuando estén listos. Este servicio se denomina simplemente $q, y como podemos ver en el ejemplo del Fragmento de Código 13 el concepto es bastante sencillo.

Básicamente, el servicio $q nos devuelve un objeto de tipo defer que será el que utilizaremos para devolver los datos. En ese mismo objeto encontramos una variable llamada promise que será lo que se devolverá de forma inmediata al controlador, y será además la encargada de esperar de forma asíncrona a que se realice la petición, y a obtener los datos en el controlador que devuelva el objeto defered del servicio mediante el método then.

Si en el controlador utilizáramos además el servicio $timeout, como podemos ver en el Fragmento de Código 14, podremos actualizar la variable languages de forma automática en caso de que el contenido del fichero JSON cambie.

Como podemos observar se crea una función llamada getLanguages que llama al servicio utilizando el sistema de promesas que vimos anteriormente. Una vez obtiene los datos, utiliza $timeout para llamarse a si misma transcurridos 5 segundos.

0