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.

1

MEAN: Node.js y Express

Siguiendo con la serie de artículos dedicados al stack MEAN, me dispongo a explicar brevemente las características de Node.js y Express, una infraestructura perfecta para empezar. En esta ocasión no entraré en demasiados detalles, y me limitaré a describir las tecnologías poniendo ejemplos simples que en artículos posteriores desarrollaré con ejemplos, una vez haya presentado todas las tecnologías del stack.

Node.js

JavaScript nació en el lado servidor casi al mismo tiempo que en el navegador. La propia empresa que creó el lenguaje, Netscape, lo incluyó también en su servidor web llamado Netscape Enterprise Server, aunque con poco éxito. Poco después Microsoft lo incorporó a su ASP clásico, donde todavía funciona.

No es sin embargo hasta hace relativamente poco, en 2009, cuando un proyecto empezó a poner en el mapa realmente a JavaScript en el lado servidor. Se trata de Node.js, un entorno de ejecución de aplicaciones multiplataforma y de código abierto (Open Source).

Node.js utiliza el motor de JavaScript de Google, denominado V8, y provee una arquitectura orientada a eventos (como la de los navegadores) así como una serie de APIs no-bloqueantes (asíncronas) que le proporcionan un rendimiento y una escalabilidad muy elevadas. Se puede utilizar para crear cualquier tipo de lógica de aplicación, pero dado que incorpora un módulo para poder actuar como un servidor web, es especialmente popular para crear aplicaciones web. Actualmente lo emplean para sus aplicaciones multitud de empresas de todos los ámbitos pero especialmente de Internet: PayPal, SAP, Groupon e incluso la mismísima Microsoft.

Gracias a Node.js se pueden crear gratuitamente aplicaciones web de alto rendimiento, en cualquier sistema operativo y utilizando como único lenguaje de programación JavaScript.

npm

Node.js cuenta con un repositorio de paquetes que incluye diferentes recursos para utilizar en la creación de aplicaciones. Para acceder a ese repositorio se utiliza el administrador de paquetes denominado npm, el cual nos permite instalar y actualizar innumerables recursos creados por diferentes desarrolladores. Además, permite crear nuestros propios paquetes y recursos de código para poder reutilizarlos y compartirlos con la comunidad.

Cada vez que instalamos un paquete en nuestra aplicación, se añadirá información relacionada en un fichero denominado package.json. Este fichero incluye toda la información importante de una aplicación Node.js en formato JSON, como el nombre y descripción de la misma, la versión actual, el tipo de licencia del mismo, y lo que es más importante, las dependencias necesarias de otros paquetes. Esto nos permite crear paquetes que podremos reutilizar en otras aplicaciones añadiendo éstos como dependencia en el fichero package.json de la nueva aplicación, así como modificar las versiones de las dependencias y demás propiedades importantes.

En el Fragmento de Código 1 podemos ver el fichero package.json de una aplicación web básica. Como podemos observar, existen algunos parámetros que son relativos a la aplicación en sí, como el nombre, la versión o la visibilidad del paquete. Otros más avanzados como scripts, que nos permite introducir comandos habituales en nuestra aplicación, lo cual nos facilitará algunas tareas comunes. Finalmente, la propiedad dependencies, como podemos deducir, incluye los nombres de los paquetes necesarios así como la versión de los mismos.

Las versiones de los paquetes incluyen algunos modificadores que nos permitirán actualizar los paquetes conforme vayan saliendo nuevas versiones, o mantener las versiones específicas de forma estricta. Los modificadores de versión más habituales son los siguientes:

  • >=: Versiones mayores o iguales a una versión concreta. Por ejemplo >=1.3 instalará todas las versiones superiores a la 1.3 sin distinción.
  • ~: Actualizaciones de la rama de la versión especificada. Por ejemplo ~1.3 indicará que se permiten actualizaciones de versiones del tipo 1.3.2, pero en caso de que el paquete se actualizara a la versión 1.4, ésta actualización sería ignorada.

Todos los paquetes que instalemos a través de npm se instalarán en la carpeta node_modules. De esta forma, en caso de que quisiéramos compartir nuestro paquete en un repositorio, o simplemente distribuirlo de forma manual, podríamos ignorar esa carpeta, ya que ejecutando el comando “npm install” se crearía automáticamente la carpeta node_modules y se descargarían del repositorio todas las dependencias indicadas en el fichero package.json. Del mismo modo, en caso de que quisiéramos actualizar las dependencias a versiones más actuales, el comando a utilizar sería “npm update”.

Una forma de utilizar estos paquetes en una aplicación es utilizando la función require, que recibe como parámetro una cadena de texto con el nombre del paquete instalado a utilizar, devolviendo un objeto que contendrá los métodos y valores correspondientes que podremos utilizar.

Servidor HTTP

Una vez instalemos Node.js en el sistema, podremos crear un servidor web completamente funcional definiéndolo en un fichero de texto utilizando los componentes connect y serve-static como podemos ver en el Fragmento de Código 2. El componente connect nos permitirá crear una instancia de servidor HTTP que se encargará de manejar las peticiones en un puerto determinado, mientras que serve-static permitirá a este servidor mostrar ficheros estáticos HTML situados en la misma carpeta del fichero app.js de definición de la aplicación (_dirname) o en cualquier otra carpeta que se le pase por parámetro a este componente en su definición.

Para iniciar el servidor tan solo hay que ejecutar el fichero con node como podemos ver en el Fragmento de Código 3. De esta forma podríamos acceder a los ficheros HTML de la carpeta mediante un navegador en la URL http://localhost:1337/fichero.html.

Existen múltiples formas de crear un servidor en Node.js, así como numerosas extensiones a las que podremos acceder con el ya mencionado administrador de paquetes para crear diferentes tipos de aplicación.

Node.js y MongoDB

Como vimos en la descripción de MongoDB, existe una pequeña diferencia a la hora de utilizar este sistema gestor de base de datos entre un sistema u otro, y es la forma de obtener un objeto de base de datos desde el que realizar las peticiones.

En Node.js podemos utilizar el paquete oficial mongodb para obtener una conexión a la base de datos y realizar peticiones similares a las vistas anteriormente utilizando un esquema de conexión parecido al del Fragmento de Código 4.

Sin embargo, existen otras extensiones más elegantes como mongoose, que nos permite definir esquemas en JavaScript para facilitar la tarea de manejo de colecciones y mantener un código fuente más modular.

Express

Para ayudar a la creación de servidores y aplicaciones web se crearon diferentes infraestructuras que facilitaban esta tarea. Entre éstas destaca Express, que forma parte del conjunto de aplicaciones MEAN, que explicaremos a continuación.

Como hemos explicado anteriormente, Node.js es el entorno ideal para crear la lógica de un gran número de tipos de aplicaciones, y puesto que posee un módulo para controlar el protocolo HTTP, es posible crear aplicaciones web de forma sencilla. Sin embargo, para aplicaciones web medianamente complejas resulta muy laborioso y complejo desarrollar toda la lógica inicial de una aplicación web haciendo uso de Node.js sin ninguna metodología o infraestructura.

Haciendo uso de npm, Express se puede instalar de forma sencilla con el siguiente comando:

Express es una infraestructura de aplicaciones web escrita en JavaScript para Node.js que nace con el propósito específico de crear aplicaciones web en este entorno. Ofrece soporte para las principales necesidades en este tipo de aplicaciones: gestión de peticiones y respuestas, cabeceras, rutas, vistas, etc. Además, cuenta con diferentes plantillas de aplicación que nos permite inicializar diferentes proyectos dependiendo de las necesidades específicas requeridas en cada caso.

Como puede verse en el Fragmento de Código 5 se pueden controlar diferentes tipos de petición HTTP estableciendo el patrón de la URL en el primer parámetro de la función, y la función de control en el segundo, que recibe como parámetros la propia petición, así como un objeto que nos permite establecer una respuesta. En el ejemplo del Fragmento de Código 5 podremos obtener las respuestas oportunas realizando las diferentes peticiones en las URL: http://localhost:3000/, http://localhost:3000/usuario y http://localhost:3000/cliente respectivamente.

Sin embargo, Express cuenta con un generador de aplicaciones que nos permite crear una estructura de aplicación completa ejecutando un simple comando, evitándonos la tarea inicial de gestionar las diferentes peticiones más habituales. Esto facilita notablemente la tarea de crear aplicaciones siguiendo una estructura lógica de carpetas, separando conceptos en diferentes ficheros para hacer más modular el código. Mediante el siguiente comando se creará una estructura de carpetas como la que podemos ver en la Captura 1.

Estructura de carpetas de una aplicación generada con Node.js y Express
Captura 1: Estructura de carpetas de una aplicación generada con Express

Como podemos observar, entre los ficheros que se generan se encuentra el fichero package.json, el cual se corresponde con el que pudimos ver anteriormente en el Fragmento de Código 1, por tanto, para terminar de inicializar la aplicación debemos instalar las dependencias que indica este fichero mediante el ya mencionado comando “npm install”. Una vez instaladas las dependencias podremos iniciar la aplicación mediante el comando del Fragmento de Código 7 que ejecutará el script de inicialización contenido en el fichero package.json que ejecuta el fichero ./bin/www con la definición del servidor HTTP.

La definición de la aplicación la encontramos en el fichero app.js, en el que se incluyen, entre otras cosas, la definición dónde encontrar las rutas disponibles en la aplicación, así como gestión de errores y manejo de vistas. Cabe destacar que, por defecto, Express utiliza un motor de plantillas denominado Jade, el cual permite generar código HTML de forma dinámica mediante un lenguaje propio que permite reutilizar código de forma sencilla. Sin embargo, este lenguaje y su uso se escapa del contexto de nuestro propósito, por lo que no nos detendremos a detallar su funcionamiento. Encontraremos las definiciones de vistas en este lenguaje en los ficheros contenidos en la carpeta views.

Por otro lado, las rutas se encuentran estructuradas de forma modular en diferentes ficheros en la carpeta routes, donde podremos ver código similar al que pudimos ver en el Fragmento de Código 6.

En los próximos artículos partiremos de una aplicación web generada con Express con la estructura anteriormente vista, sin embargo, para procurar que nuestro servidor HTTP esté funcionando de forma constante en la máquina, y de esa manera pueda interactuar con otro tipo de servidores y aplicaciones instalados, haremos uso de la aplicación pm2, contenida en el repositorio de aplicaciones npm, que se encargará de mantener nuestra aplicación web ejecutándose de forma continua.

1

MEAN: MongoDB

Continuando con la serie de artículos relacionados con el stack MEAN (MongoDB, ExpressJS, AngularJS y NodeJS), y tras haber explicado lo que es JSON, paso a explicar los por menores del sistema gestor de bases de datos MongoDB, no sin antes introducir el concepto de NoSQL.

NoSQL

En el lado del almacenamiento se han utilizado tradicionalmente bases de datos relacionales. Sin embargo, actualmente, los tipos de información que suelen requerir las aplicaciones web demandan mayor flexibilidad, menos coherencia y sobre todo mayor capacidad de escalar. Para dar respuesta a todo esto surge la tendencia tecnológica en almacenes de datos que se denomina NoSQL. Aunque el nombre pueda llevar a confusiones, su significado real es “no solo SQL” (Not Only SQL), pues su característica principal es que no utilizan SQL como lenguaje principal para las consultas.

En una base de datos NoSQL los datos almacenados no requieren de estructuras fijas, por lo general tampoco soportan operaciones JOIN y no cumplen completamente las formas normales que garantizan atomicidad, consistencia, aislamiento y durabilidad.

Esta ruptura con las formas tradicionales nace de las nuevas necesidades en el tratamiento de datos. Con el crecimiento de la red y con ello el crecimiento exponencial de la cantidad de datos, se hacía necesario una nueva forma de procesar grandes volúmenes de datos con un mayor rendimiento y que permitiera un procesamiento en tiempo real más eficiente. Estos requerimientos se hicieron mucho más importantes que la coherencia de datos, que eran la principal característica de los sistemas de base de datos clásicos. De esta forma, los sistemas NoSQL basan su optimización en las operaciones de agregar y recuperar datos más allá de la de mantener la consistencia de la información almacenada.

A pesar de que los sistemas NoSQL ofrecen grandes ventajas para algunas estructuras de datos no hay que tomar esta nueva tecnología como un estándar para todos los sistemas que se vayan a desarrollar en el futuro. Los sistemas de base de datos relacionales siguen cumpliendo su papel, y lo seguirán haciendo en la mayoría de los sistemas en los que se utiliza. Es por ello que antes de tomar la decisión de si utilizar un sistema tradicional o un NoSQL habrá que realizar una tarea de análisis para determinar cuál es el mejor gestor de base de datos a utilizar dependiendo de las características del sistema que vayamos a desarrollar.

Almacenamiento de datos NoSQL

Los almacenes de datos NoSQL pueden ser de diversos tipos. Dependiendo de cómo almacenen la información y manejen los datos se clasificarán dentro de uno de los siguientes grupos:

  • Documentales
  • De Grafo
  • Clave-Valor
  • Multivalor
  • Orientados a Objetos
  • Tabulares

MongoDB

De entre todos los almacenes de datos no-relacionales hay uno que destaca especialmente: MongoDB, que es la «M» del stack MEAN.

En la clasificación de bases de datos NoSQL podríamos clasificar a MongoDB entre las bases de datos documentales y de clave valor, ya que en el tratamiento de los datos realiza una mezcla de ambos sistemas. Puesto que está orientado a documentos, lo que se almacena en una base de datos de tipo MongoDB es, valga la redundancia, documentos en BSON, que no es más que una implementación binaria del formato JSON, por lo que todos los datos almacenados en la base de datos se podrían tratar de una forma similar a como se hacen en JavaScript, que será el lenguaje que utilizará estos datos.

Los documentos se almacenan en colecciones, que es lo más parecido a las tablas de los sistemas de bases de datos tradicionales. Sin embargo, a diferencia de las tablas, los elementos de una colección no tienen porqué tener los mismos campos. En el Fragmento de Código 1 podemos ver un ejemplo de colección válida almacenada en BSON con mongoDB. Hay que tener en cuenta que mongoDB añade un campo adicional a todos los documentos de una colección. Este campo se denomina “_id” y es un valor numérico autoincremental de carácter único que sirve de identificador.

Cómo puede verse, no existe un esquema definido, por lo que habrá que plantear la forma de almacenar los datos de una manera diferente a cómo se hace con las bases de datos relacionales, pues, como puede deducirse, no solo carecemos de esquemas definidos, sino también de relaciones.

Almacenamiento de Datos

Si quisiéramos almacenar los datos de una tienda online en la que existen clientes y pedidos podríamos diseñarlo de las siguientes formas, cada una con sus ventajas y desventajas:

Una única colección

Podemos almacenar en una misma colección los datos de los clientes, e incluirles un campo pedidos en el que almacenaríamos una lista con todos los pedidos del cliente. En el Fragmento de Código 2 podemos ver cómo quedaría en la base de datos.

Almacenando los datos de esta forma conseguiremos optimizar las consultas de pedidos de cada cliente, sin embargo, habrá que tener en cuenta, que cuantos más pedidos haga un cliente, mayor crecerá el tamaño de éste, y cada vez que hagamos una consulta de un cliente todos esos datos serán manejados. A largo plazo no parece una buena solución dependiendo del sistema que se vaya a desarrollar.

Dos colecciones

Podemos optar por un diseño de base de datos similar al que utilizaríamos en una base de datos relacional, esto es, utilizando una colección para clientes, y otra para pedidos. En el Fragmento de Código 3 se muestra un ejemplo de cómo se almacenarían los datos en este caso.

Aunque el uso de esta estructura nos pueda parecer más familiar y coherente, debemos tener en cuenta que al no tratarse de un sistema de base de datos relacional, para obtener el conjunto de pedidos de un cliente, anteriormente hemos de obtener el cliente para poder conocer el campo de identificación que lo relaciona con cada pedido, por lo que deberíamos realizar dos peticiones en lugar de una.

Dos colecciones con datos duplicados

Dependiendo del sistema podríamos optar por un diseño híbrido de las dos alternativas anteriores. Supongamos que necesitáramos consultar de forma recurrente el último pedido de un cliente. En este caso sería conveniente almacenar ese pedido en el mismo documento del cliente, además de en la tabla general de pedidos. De esa forma ahorraríamos un gran número de peticiones habiendo tenido en cuenta esa consulta recurrente, sacrificando por otro lado el inconveniente de duplicar la información. En el Fragmento de Código 4 podemos ver un ejemplo de cómo se almacenarían los datos en este caso.

En definitiva, como podemos observar el sistema es muy flexible, y nos permite diseñar siempre la opción más óptima dependiendo del sistema, haciendo en este caso que el sistema de base de datos se adapte a las necesidades del mismo, en lugar de ser el sistema el que se tenga que adaptar a las características de un sistema gestor de base de datos concreto.

Es importante destacar, que escojamos la opción de diseño que escojamos, podremos cambiarla en cualquier momento sin que esto afecte a los datos ya almacenados, lo que supone una gran ventaja a la hora de escoger este sistema.

Operaciones de Consulta

Mientras que en los sistemas tradicionales podíamos utilizar SQL como lenguaje de consulta, en MongoDB no tenemos ese privilegio. En este sistema todas las consultas se realizan mediante JavaScript. Por tanto, en MongoDB no existen cadenas de consulta, si no que existen funciones que aceptan parámetros específicos para realizar las consultas. Las funciones de consulta más utilizadas son find, findOne y findAndModify. En los siguientes apartados veremos un ejemplo de uso de las mismas. Para poder entenderlos debemos definir algunos objetos JavaScript que se utilizarán:

  • db: Objeto de conexión de base de datos. Dependiendo del sistema a utilizar (Express, C#, etc.) se creará de una forma u otra. Todas las peticiones parten de este objeto. Dependiendo del sistema accederemos a las colecciones de una forma u otra, pero en la mayoría de los casos el esquema es muy similar al que veremos a continuación.
  • Colección usuarios: Definimos una colección a la que llamaremos “usuarios” con la estructura del Fragmento de Código 5.

find

Este método es el que nos permite buscar elementos en una colección. Si no le pasamos ningún parámetro nos devolverá todos los elementos de la misma.

Sin embargo, hay que tener en cuenta que este método no nos devolverá de forma automática todos los elementos de una colección, si no que nos devolverá una especie de puntero a los datos solicitados que podremos recorrer de forma sencilla. A este objeto se le denomina cursor.

Cursor

En mongoDB un cursor es una conexión directa con la base de datos que permanece abierta mientras la necesitamos, permitiéndonos iterar sobre los resultados de una consulta concreta. Como ejemplo, para recorrer todos los objetos de una colección podríamos hacer algo parecido a lo que nos encontramos en el Fragmento de Código 6.

Los cursores cuentan con diferentes métodos adicionales que nos pueden servir para diferentes finalidades. Entre ellos destaca la función limit, que acepta como parámetro un número y nos permite limitar el número de resultados, y sort, que nos permite ordenar los resultados conforme a parámetros específicos. La función sort recibe como parámetro un objeto JSON con los campos por los que se deberá ordenar, cada uno con dos posibles valores: 1 en caso de que queramos que se ordene de forma ascendente por ese campo, o -1 en caso contrario. En el Fragmento de Código 7 podemos ver un ejemplo completo de los métodos limit y sort.

El resultado serán los dos primeros objetos de la colección ordenados de forma ascendente por el campo age, y de forma descendente por el campo name.

Filtros

Para poder filtrar los resultados de una consulta debemos pasar como parámetro al método find un documento JSON con las características que deben cumplir los objetos a encontrar. Este procedimiento sería similar a la cláusula WHERE de las sentencias SQL. En el Fragmento de Código 8 podemos ver un ejemplo de uso de un filtro simple que nos devolvería un cursor con el usuario con edad 32 y nombre Catalina Sánchez Román.

Existen diferentes operadores que nos permiten modificar estos filtros para optimizar las búsquedas de forma sencilla.

Proyecciones

Hasta ahora la función find está devolviendo documentos completos de una colección. En SQL sería el equivalente a realizar consultas comenzando con SELECT *. Sin embargo, al igual que con SQL, podemos filtrar los resultados para obtener solo los campos que necesitemos. Para ello haremos uso de las proyecciones, que se pasarán en el segundo parámetro de la función find. Una proyección es un objeto JSON que contiene como pares clave/valor los nombres de los campos a obtener con valores 0 o 1. Utilizaremos el valor 1 en caso de que queramos que ese valor se obtenga, o un 0 en caso contrario. Por defecto, en caso de que pasemos una proyección como parámetro, todos los valores que no definamos de forma explícita tendrán el valor 0, excepto el campo _id, el cual, si no queremos que se encuentre entre los campos devueltos habrá que especificar claramente que debe ser así, como podemos observar en el Fragmento de Código 9, el cual, nos devolverá tan solo los campos name de la colección.

findOne

Este método es parecido al método find, pero en lugar de obtener un cursor, obtendremos un único documento en forma de objeto JavaScript. Si la consulta de búsqueda contiene más de un objeto que cumpla con las condiciones que hayamos establecido, la función findOne nos devolverá el primero. En el Fragmento de Código 10 tenemos un ejemplo de uso con filtros y proyecciones.

findAndModify

En ocasiones se hace necesario modificar un documento y obtener el resultado de la modificación de inmediato. De esta forma certificamos que la modificación se ha hecho correctamente y podemos seguir operando con el objeto en cuestión manteniendo de esa forma cierta consistencia. En este caso, los parámetros se pasarán como un objeto JSON con los siguientes campos:

  • query: Un objeto JSON con los criterios de búsqueda, similar a los filtros de los métodos find y findOne.
  • sort: Un objeto JSON con los criterios de ordenación similar a los parámetros del método sort de los cursores.
  • remove: Campo booleano. Si el valor es true, se borrará el documento encontrado. No es necesario incluirlo si se incluye el campo update.
  • update: Un objeto JSON con los campos a modificar y sus valores. Este campo no es necesario en caso de utilizar el campo remove.
  • new: Campo booleano. Si es true, el método findAndModify devuelve el objeto modificado, en caso contrario devuelve el original antes de ser modificado.
  • fields: Un objeto JSON con la proyección de los datos que queremos obtener.
  • upsert: Campo booleano. Si es true y la consulta no devuelve ningún documento creará uno nuevo con los campos del parámetro update. Si es false no ser hará nada.

En el Fragmento de Código 11 podemos ver una forma de modificar el primer documento con el campo age igual a 32 ordenado de forma descendente por el name cambiándole el name y obteniendo el objeto ya modificado. Como puede deducirse, findAndModify es el método que nos sirve tanto para actualizar datos, como para eliminarlos.

Operaciones de Actualización de Datos

Todas las bases de datos son capaces de realizar operaciones CRUD (Create, Read, Update y Delete). En el apartado anterior hemos visto diferentes formas de realizar opciones de lectura, por lo que nos quedaría ver cómo se insertan y modifican datos en MongoDB.

Inserción

La inserción es uno de los comandos más simples de MongoDB, ya que tan solo hay que utilizar el método insert y pasarle como parámetro un objeto JSON. En el Fragmento de Código 12 podemos ver un ejemplo de inserción de un nuevo usuario en la colección usuarios.

Como mencionamos anteriormente, MongoDB añade automáticamente un campo identificador denominado _id a todos los objetos que se insertan. Este campo se puede añadir en un objeto de inserción sin ningún problema, teniendo en cuenta que en caso de introducir un valor que coincida con alguno ya almacenado en la colección, MongoDB nos devolverá una excepción.

Existe otra función que nos permite introducir objetos en una base de datos pero que, en caso de añadir un campo _id que ya exista en la colección, en lugar de devolver una excepción, modificaría el objeto que coincidiera con esa misma _id introducida. Esta función se denomina save y se utiliza de la misma forma que se hace con insert.

Para añadir varios objetos a la vez debemos pasar como parámetro un array JSON con los objetos a añadir, como podemos observar en el Fragmento de Código 13.

Borrado

Para eliminar documentos de una colección utilizaremos el método remove, que recibe como parámetro un objeto JSON con los criterios de selección de los elementos a borrar como podemos ver en el Fragmento de Código 14, dónde se eliminarían todos los objetos de la colección cuyo campo age sea igual a 32 y cuyo campo mail sea “miguel@smendoza.net”. En caso de no pasarle ningún parámetro se borraría la colección completa.

Actualización

En el apartado anterior sobre Operaciones de Consulta, vimos un ejemplo de cómo actualizar un documento haciendo uso de la función findAndModify. Sin embargo, existe otra forma más directa de actualizar un documento, y es haciendo uso de la función update. Esta función recibe como parámetros un objeto con los criterios de selección del elemento o elementos a modificar, y un objeto JSON con el nuevo objeto a sustituir. El Fragmento de Código 15 muestra un ejemplo de cómo sustituir todos los documentos cuyo campo age sea 32 por un objeto diferente, es decir, no se realizaría una actualización de los campos propiamente dichos dentro del objeto que coincida con los criterios, si no que el objeto sería sustituido por completo por el nuevo pasado por parámetro, sea como sea su estructura.

Como puede observarse éste no es el comportamiento habitual de una operación de actualización, ya que estamos acostumbrados a utilizar este tipo de funciones para actualizar campos concretos de un objeto en una base de datos, en lugar de sustituirlos por completo. Para lograr un comportamiento similar al de las sentencias UPDATE en SQL deberemos utilizar un operador de MongoDB denominado $set. Éste nos permitirá actualizar campos concretos de un objeto que coincida con los criterios de búsqueda introducidos en el primer parámetro. En el Fragmento de Código 32 podemos ver como modificar el campo mail del documento con valor del campo _id igual a 1.

Instalación

Para instalar una base de datos mongoDB tan solo deberemos seguir los pasos que podremos encontrar en el siguiente enlace:

https://docs.mongodb.com/manual/installation/

La instalación es un proceso bastante sencillo, por lo que no nos detendremos en este momento a detallar los por menores de la misma, pero resulta de vital importancia entender que una vez instalado en nuestro sistema, podremos acceder a la base de datos mediante línea de comandos y ejecutar todos los comandos que veremos a continuación. En la mayoría de sistemas esto se consigue ejecutando el comando «mongo» en un terminal.

Recomiendo utilizar una interfaz de usuario de administración (Admin UI) para manejar los datos de forma más eficiente. Entre todas las posibilidades yo destacaría Robomongo, ya que su facilidad de uso y posibilidades superan con creces a las demás alternativas. En cuanto a interfaz de usuario basada en web recomiendo utilizar mongo-express. La utilización y posibilidades de estas herramientas escapan al contexto del artículo, por lo que os animo a investigar una vez hayáis entendido bien los conceptos.

Conclusión

Como hemos podido ver, MongoDB es un sistema gestor de base de datos muy flexible y sencillo de utilizar. Todas las sentencias que hemos visto podríamos utilizarlas tanto en el terminal de comandos del propio sistema de Mongo, como a través de cualquier framework compatible.

En los siguientes artículos, veremos como utilizar MongoDB desde NodeJS y como integrarlo en un entorno real.

1

MEAN: JSON

Hace cosa de un año decidí sacarme la titulación del Grado en Ingeniería Informática convalidando gran parte del mismo con mi Ingeniería Técnica en Informática. Realmente solo tuve que hacer dos asignaturas, y presentar un TFG o Trabajo de Fin de Grado. Aproveché el TFG para sumergirme en el mundo del desarrollo de aplicaciones web de tipo SPA (Single Page Application) en Angular 1.x utilizando el stack MEAN (MongoDB, Express, AngularJS y NodeJS), para lo que desarrollé una aplicación web que ahora se utiliza en mi entorno laboral de forma frecuente.

Puesto que no supe hacerlo de otra manera, toda la memoria de mi TFG se convirtió en un gran tutorial que pretendía recoger todos los conocimientos necesarios para que cualquier iniciado pudiera desarrollar su aplicación web. Pasado un tiempo, y después de haberme encontrado de nuevo con el documento en sí, me ha parecido buena idea compartir todos los contenidos de esa memoria de forma estructurada, por lo que iré creando diferentes artículos con los distintos capítulos del mismo.

Intentaré partir de los conocimientos más básicos necesarios para ir comprendiendo las diferentes tecnologías que utilicé, por lo que considero primordial comenzar hablando del formato de datos utilizado en todos los intercambios de información de la arquitectura MEAN. Este estándar es el llamado JavaScript Object Notation, o comúnmente denominado JSON (ECMA-404).

JSON es una alternativa más ligera al XML como formato de intercambio de datos, ya que no necesita de etiquetas que encierren la información, si no que se sirve de una notación simple basada en comillas, llaves y corchetes que hacen que la información no solo sea más liviana, si no más fácilmente entendible.

Estructuras

En JSON existen dos tipos de estructuras básicas:

Objeto

Colección de pares clave/valor encerrados entre llaves. También es denominado diccionario o array asociativo. En la Figura 1 se muestra la definición formal de un objeto JSON.

Esquema de un Objeto JSON
Figura 1: Objeto JSON

Un ejemplo de objeto JSON sería el siguiente:

Array

Colección ordenada de valores encerrados entre llaves. En la Figura 2 podemos ver que la diferencia fundamental con un objeto.

Estructura de un Array JSON
Figura 2: Array JSON

A continuación podemos ver dos ejemplos de arrays en JSON:

Datos

Resulta importante destacar que los valores que se pueden utilizar en una estructura JSON son números y cadenas de texto, los valores booleanos true o false, null como valor nulo, además de los ya mencionados objetos y arrays. En la Figura 3 podemos ver la definición formal de valor en JSON.

Estructura de Valores en JSON
Figura 3: Valores en JSON

La especificación de JSON define además el formato de las cadenas de texto, por lo que habrá que tomar especial atención a la hora de crearlas, ya que no se permiten caracteres especiales como tal, si no que hay que utilizar notación UNICODE. En la Figura 4 podemos ver un ejemplo de cómo se forma una cadena de texto JSON.

Estructura de Cadenas de Texto en JSON
Figura 4: Cadenas de Texto en JSON

Los números en JSON no necesitan introducirse entre comillas, pero si tienen que cumplir ciertas reglas de formato como se puede observar en la Figura 5.

Estructura de Números en JSON
Figura 5: Números en JSON

Para finalizar, en el siguiente ejemplo podemos ver un extraño objeto JSON con diferentes elementos:

1