AngularJS

Clon de Spritz en AngularJS: Fast Reader

Durante estas semanas ha salido a la luz un método de lectura en el ordenador y dispositivos móviles que parece estar llamado a revolucionar la forma de leer textos: presentan el texto palabra por palabra en un cuadro, centrando cada palabra en una posición. De este modo se evita perder tiempo en enfocar la vista en la siguiente palabra. Se puede encontrar más información en muchas noticias, como por ejemplo aquí, o en su sitio oficial: http://www.spritzinc.com/

La parte mala es que todavía no está disponible, más allá de una simple demo. Así que como me gustaría utilizarlo ya mismo, y no voy a apuntarme a su cartera de beta-testers, he decidido usarlo como excusa para hacer una webapp usando AngularJS  y Bootstrap.

Si queréis probar mi implementación, lo podéis hacer en http://rusizate.com/reader/ y como siempre el código fuente en el respositorio de GitHub: https://github.com/4lberto/FastReader

lectura

En cuanto a la parte técnica es muy sencilla. Sigue estos pasos:

  1. Lee el texto del textarea, que se guarda en una variable.
  2. Lo transforma a un array de palabras
  3. Va mostrando cada cierto tiempo las palabras en pantalla, calculando dos cosas:
    1. Qué letra se va a iluminar en rojo: mediante un switch case, dependiendo de la longitud de la palabra.
    2. Cuántos espacios se va a insertar delante de la palabra para que quede centrada respecto de la letra iluminada.

Otros dos aspectos destacables:

  • Control del tiempo en AngularJS: mediante el componente $timeout, que es como el window.setTimeout de Javascript. Es decir, espera una serie de milisegundos para llamar a una función. Metido en un bucle y mientras haya palabras por mostrar se va ejecutando. es muy importante usar $timeout y no window.setTimeout en AngularJS. El método tradicional no funciona.
$timeout(loop, Math.floor(((palabra.length+2)/4)*$scope.speed));
  • Necesitamos mostrar una palabra que tiene código HTML dentro. Para pintar de rojo la letra, debemos rodearla con un span. Para ello , en angular se puede usar $sce.trustAsHtml(palabra) y luego en el atributo de la vista podemos usar: ng-bind-html=”palabraActual”
<h3 ng-bind-html="palabraActual">{{palabraActual}}</h3>

 

$scope.palabraActual=$sce.trustAsHtml('pal<span class="rojo">a</rojo>bra');

En cambos casos tenemos que inyectar $timeout y $sce en el controlador de AngularJS


function TodoCtrl($scope, $timeout, $sce) {

El controlador al final queda de este modo:

</pre>
function TodoCtrl($scope, $timeout, $sce) {

var textoSplit;
 $scope.palabraActual=$sce.trustAsHtml("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a-word");
 $scope.indicePalabra=0;
 $scope.textoArea = "There is something at the bottom of every new human thought, every thought of genius, or even every earnest thought that springs up in any brain, which can never be communicated to others, even if one were to write volumes about it and were explaining one's idea for thirty-five years; there's something left which cannot be induced to emerge from your brain, and remains with you forever; and with it you will die, without communicating to anyone perhaps the most important of your ideas. - Fyodor Dostoyevsky, The Idiot"
 $scope.speed = 120;
 $scope.speedFactor = 10;
 $scope.leyendo = false;

$scope.startReading = function()
 {
 $scope.leyendo = true;

if(!((textoSplit)&&(textoSplit.length>0)))
 {
 $scope.indicePalabra=0;
 textoSplit = $scope.textoArea.split(" "); //Separa el texto
 }

loop();

}

$scope.stopReading = function()
 {
 $scope.leyendo = false;
 }

var loop = function()
 {
 console.log("Han llamado a loop:" + textoSplit.length + " - indicePalabra" + $scope.indicePalabra);

if(textoSplit.length>$scope.indicePalabra)
 {

var palabra = textoSplit[$scope.indicePalabra];
 var puntoCentral = calculaPuntoCentral(palabra);

$scope.palabraActual = $sce.trustAsHtml(generaEspacios(puntoCentral)+colorea(palabra, puntoCentral));

$scope.indicePalabra++;
 if($scope.leyendo)
 $timeout(loop, Math.floor(((palabra.length+2)/4)*$scope.speed));
 }
 else
 {
 console.log("Fin lectura...");
 }
 }
 $scope.speedUp = function()
 {
 $scope.speed += $scope.speedFactor;
 }

$scope.speedDown = function()
 {

$scope.speed = Math.max(0,$scope.speed-$scope.speedFactor);
 }

$scope.clearTextArea = function()
 {
 $scope.textoArea="";
 textoSplit = [];
 $scope.indicePalabra=0;
 $scope.palabraActual=$sce.trustAsHtml("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a-word");
 $scope.leyendo = false;
 }
 var calculaPuntoCentral = function(palabra)
 {

var puntoCentral = 1;

switch ( palabra.length) {
 case 1:
 puntoCentral = 1;
 break;
 case 2:
 case 3:
 case 4:
 case 5:
 puntoCentral = 1;
 break;
 case 6:
 case 7:
 case 8:
 case 9:
 puntoCentral = 2;
 break;
 case 10:
 case 11:
 case 12:
 case 13:
 puntoCentral = 3;
 break;
 default:
 puntoCentral = 4;
 };

return puntoCentral;
 }
 var generaEspacios = function(puntoCentral)
 {
 var espacios = 10;
 espacios = espacios - puntoCentral;
 var esp = "";
 for(var i = 0;i<espacios;i++)
 {
 esp+="&nbsp;"
 }

return esp;
 }
 var colorea = function(palabra, indice)
 {
 if (palabra.length <2)
 return '<span class="rojo">' + palabra + '</span>'
 else
 return palabra.substr(0,indice-1) + '<span class="rojo">' + palabra.substr(indice-1,1) + '</span>' + palabra.substr(indice,palabra.length);

}

}
<pre>

El código fuente completo está en el respositorio de GitHub: https://github.com/4lberto/FastReader y la demo en http://rusizate.com/reader/

Anuncios

$resource para comunicación RESTful en AngularJS

En el post anterior hemos visto cómo se podía hacer una comunicación con un servidor RESTful mediante el objeto $http de AngularJS. Este objeto proporcionaba una interfaz para las llamadas AJAX parecida a la que se puede hacer con JQuery o Prototype.

En realidad referirse a la anterior como comunicación como RESTful era un tanto específica, puesto que se trataba de un caso particular. En realidad eran llamadas AJAX y no tenían por qué hacerse a un servidor RESTful específicamente: cualquiera que admitiera una llamada AJAX valdría.

Por recordar, dentro del controlador teníamos algunas funciones como estas.

$http.get("http://127.0.0.1:5984/dibujo/"+id).success(function(datos)
{
//Operaciones con el objeto JSON datos obtenido de la llamada
});

$http.post("http://127.0.0.1:5984/dibujo", {"e":$scope.elementos,"t":new Date().getTime()}).success(function(datos)
 {
//Operaciones una vez se ha enviado el objeto en formato JSON
 });
<span style="line-height: 1.5em;">

Es decir, llamadas GET y POST indicando la URL directamente, e incluso, si hacía falta, componiendo la URL final con el id a base de concatenación de cadenas de texto. También existían otro tipo de llamadas como DELETE, HEAD o PUT (http://docs.angularjs.org/api/ng.$http).

AngularJS es un framework centrado en facilitar la vida a los programadores, con un nivel de abstracción bastante elevado y la comunicación con un servidor puramente RESTful es una buena candidata a automatizar tareas.

Mediante $resource, AngularJS pone a nuestra disposición objetos para interactuar con un servidor que soporte el protocolo RESTful. Este objeto encapsula los mecanismos de comunicación RESTful, evitando tener que bajar al nivel de hacer llamadas mediante $http. Para que funcione es necesario utilizar el módulo ngResource (ver cómo instalarlo en http://docs.angularjs.org/api/ngResource).

Así pues, usaremos el objeto proporcionado por $resource para representar la comunicación con el servidor para un modelo de datos. En realidad encapsula los datos y le añade además una serie de propiedades o funciones para controlar la comunicación con el servidor.

Si continuamos el ejemplo que vengo usando en este blog de la página de dibujo al pasar el ratón sobre un grid, tenemos que, básicamente, el objeto consistía en dos campos: “e” para mantener un objeto que contiene como métodos las coordeadas de los elementos sobre los que había pasado el ratón y el número de veces que lo había hecho; y “t” para indicar la fecha, en milisegundos en la que se habían salvado los datos.

Analizando la siguiente imagen de DevTools de Chrome, se puede ver que “element” es un objeto devuelto por $resource y que encapsula los datos descritos para el dibujo. Podemos ver:

  •  “e” y “t” como se han descrito. Que son los datos que nos devuelve el servidor y que nos interesa.
  •  “_id” y “_rev” que son el id y la versión del documento tal y como lo proporciona CouchDB (revisar post anteriores para más información).
  •  __proto__ que son las operaciones que se añaden y que permiten la comunicación con el servidor RESTful. Como se puede ver, coinciden con los operadores por defecto.

resource1

 

Por lo tanto, el objeto “element” encapsula por una parte la información obtenida de la llamada, y por otra parte añade las operaciones para la comunicación RESTful. Además añade otros elementos como por ejemplo un $promise para implementar la carga asíncrona de información por la llamada AJAX.

Vamos a ver cómo podemos implementarlo.

En primer lugar hay que inyectar el recurso en el controlador de la aplicación:

app.controller("ctrlDibujo", ["$scope","$http", "$resource", ,function($scope, $http,$resource)

De este modo podremos usar la factoría $resource dentro de nuestro código.

La primera forma es imitando a una llamada XHR normal y corriente. No hace uso de la potencia de $resource pero nos da una idea de su uso. Como se puede ver se indica la dirección del servidor así como la URL completa para cargar un objeto determinado:

var Resultado = $resource('http://127.0.0.1:5984/dibujo/9181ffd10aa47c52e1cee65ce0000195').get();

Esto no es realmente útil. Lo ideal sería poder parametrizar la llamada y poder referirnos con el identificador RESTful. Sería algo así:

var Resultado = $resource('http://127.0.0.1:5984/dibujo/:id', {id:"9181ffd10aa47c52e1cee65ce0000195"}).get();

En este caso se puede ver cómo “:id” será sustituído por el valor de “id” del objeto pasado por parámetro. Para el ejemplo se ha completado de forma manual, pero seguro que sabes cómo asignarle una variable.

En ambos casos el resultado es un objeto que contiene la información devuelta por el servidor más las operaciones que le permiten una sencilla comunicación con el servidor.

Una cosa que llama la atención es la asignación a la variable Resultado. Al tratarse de una llamada asíncrona, AngularJS devuelve un promise cuando se realiza la operación GET de modo que la carga de la información a la variable se realizará cuando sea posible. Por tanto no podemos utilizar posteriormente estos datos en la lógica de negocio. No obstante si necesitamos realizar alguna operación, siempre tenemos la opción de usar las funciones de CallBack:

var Resultado = $resource('http://127.0.0.1:5984/dibujo/:id', {id:"9181ffd10aa47c52e1cee65ce0000195"}).get(function(elemento){//...});

Otra de las formas de utilizar $resource es usando un servicio en la aplicación para luego inyectarlo dentro de nuestro controlador. Por ejemplo:

app.factory('ElementoResource', ['$resource', function($resource){
return $resource('http://127.0.0.1:5984/dibujo/:id',{id:"@_id"});
}]);

No debemos olvidar usar el servicio dentro de nuestro controlador:

app.controller("ctrlDibujo", ["$scope","$http", "$resource", "ElementoResource",function($scope, $http,$resource, ElementoResource)

Dentro del $resource del servicio se ha espeficado que el parámetro “id” se lee del campo “_id” del objeto al que arropa. Esto vendrá perfecto para la comunicación con el servidor CouchDB que usa este valor.

Usarlo es sencillo:

 $scope.objetoResource = ElementoResource.get({id:"9181ffd10aa47c52e1cee65ce0000195"}, function(element, $scope)
 {
element.t++;
element.$save();
 });

En este ejemplo se ha pasado el objeto {id:”9181ffd10aa47c52e1cee65ce0000195″} para indicar qué elemento recuperar; se ha aumentado en 1 el valor de su parámetro “t” y se ha vuelto a grabar. Como se puede ver es algo realmente sencillo de utilizar.

Nótese cómo se ha utilizado el Callback para manipiular el objeto.

También es posible crear un nuevo objeto y comunicarse con el servidor:

var nuevoElemento = new ElementoResource();

nuevoElemento.e = {"a1":12};
nuevoElemento.t = new Date().getTime();

nuevoElemento.$save();

nuevoElemento.t = 100;
nuevoElemento.$save();

En este caso se puede ver cómo se ha creado un nuevo objeto usando el servicio, se le han asignado unas propiedades “e” y “t” y luego se ha salvado con el servidor RESTful. Posteriormente se ha modificado la propiedad “t” asignándole 100 de valor y se ha vuelto a salvar.

Sin usar servicio de por medio también es muy directo su uso, y nos evita mucho trabajo respecto a si usáramos $http

var Elemento = $resource('http://127.0.0.1:5984/dibujo/:id');

var nuevoElemento = Elemento();
nuevoElemento.t=23;
nuevoElemento.$save();
...

En resumen, $resource de AngularJS permite comunicar nuestra aplicación con un servicio RESTful de una manera muy sencilla y eficiente.

CouchDB como base de datos para AngularJS

Vamos a ampliar el ejemplo anterior hecho con AngularJS (https://mysticalpotato.wordpress.com/2014/01/31/creando-una-sencilla-aplicacion-con-angularjs-y-yeoman/) con una interesante funcionalidad que nos permitirá guardar el estado del dibujo que hayamos hecho en un momento determinado y claro está, volver a recuperarlo más adelante.

Como se vio en el post anterior, se utiliza una estructura de objetos en Java (por tanto convertible a formato JSON) para almacenar el número de veces que se había visitado cada una de las celdas del ejemplo. De este modo, si almacenamos este objeto, podremos reproducir fielmente la posición en un momento determinado.

Existen diferentes opciones para guardar los diferentes estados para nuestra aplicación. Por ejemplo podemos utilizar el localstorage de HTML5 o también podemos usar una base de datos. En este último caso lo mejor es tratar con una base de datos que pueda manejar la información en formato JSON. Existen múltiples alternativas, pero en este caso nos vamos a decantar por CouchDB.

Usar CouchDB para este ejemplo nos ofrece varias ventajas. La primera es que es una base de datos NoSQL que maneja documentos JSON de forma nativa. Otra ventaja es que para interaccionar con ella, monta un servidor RESTful a través de HTTP. Esto facilita enormemente las llamadas AJAX que tenemos que hacer en AngularJS.

Vamos pues en primer lugar a ver cómo se instala y se crea la base de datos para salvar el estado del dibujo en cada momento con CouchDB. Más adelante veremos lo fácil que es modificar utilizar AngularJS para soportar esta nueva funcionalidad.

Seguimos los siguientes pasos:

  1. Descargar la última versión de CouchDB (al momento de escribir este artículo era la 1.5.0). Accedemos a su sitio web y descargamos el fichero. Están en http://couchdb.apache.org/
  2. Instalamos CouchDB. Muy fácil, sólo hay que indicar el lugar del disco duro donde lo pondremos. A vuestro gusto. Una cosa que no he hecho es hacer que se arranque como servicio.
  3. Accedemos al directorio de instalación/bin y ejecutamos CouchDB.bat . Así se arranca el servidor, que responde en http://127.0.0.1:5984/

Si queremos podemos probar a llamarlo desde un navegador y hacer llamadas. También podemos usar cURL para invocar las acciones de la API. Tienes una información detallada en la documentación oficial de esta base de datos: http://docs.couchdb.org/en/latest/intro/api.html

Por simplificar el proceso, CouchDB incluye un gestor Web integrado en su servidor. Su acceso es muy fácil, http://127.0.0.1:5984/_utils/ . Este manager se llama “Futon”. Como dato de la versatilidad de CouchDB, esta base de datos permite almacenar documentos y servirlos como si de un servidor web se tratase. Futon es una aplicación Web que sirve la base de datos, e incluso nosotros podremos incluir nuestra aplicación (HTML+CSS+JS) y hacer que CouchDB la sirva a los navegadores. Son los llamados Design Documents (http://guide.couchdb.org/draft/design.html).

Veamos cómo crear la base de datos dentro de CouchDB.

En primer lugar creamos una base de datos para almacenar la información. Entramos en Futon (http://127.0.0.1:5984/_utils/) Pulsamos sobre “Create Database…” y le damos el nombre “dibujo”. Aquí se almacenarán los objetos Javascript que determinan el estado de cada celda del dibujo, esto es, el número de veces que se ha pasado el ratón por encima.

couch1

Si abrimos el navegador e introducimos la dirección http://127.0.0.1:5984/dibujo podremos ver información acerca de la base de datos recién creada. Después de varios usos podremos obenter algo así:

{"db_name":"dibujo","doc_count":28,"doc_del_count":0,"update_seq":30,"purge_seq":0,"compact_running":false,"disk_size":90216,"data_size":13301,"instance_start_time":"1391813178196821","disk_format_version":6,"committed_update_seq":30}

Sin entrar mucho en detalle, CouchDB almacena por cada uno de los registros un identificador (_id) y un número de revisión (_rev). El Id se asigna autmáticamente si no indicamos nada, lo mismo que el número de revisión (_rev). Sí, aquí se almacenan diferentes versiones de un mismo registro, de modo que hay un histórico de datos. Estos dos valores son básicos, y se les tienen que unir el resto de datos que vamos incluyendo. Al final podríamos obtener algo así:

{
 "_id": "9cd4cfb4841782207f7f3460f1002285",
 "_rev": "3-313a13f005e452222cbc1e4aca28121a",
 "e": {
 "a2": 2,
 "b2": 2,
 "c2": 2,
 "d6": 1,
 "c6": 1,
 "b6": 2,
 "b7": 3,
 "a7": 2,
 "c3": 1,
 "d3": 1,
 "d4": 1,
 "c4": 1,
 "c5": 1,
 "b5": 1,
 "a8": 1,
 "a9": 1,
 "c7": 1,
 "d7": 1,
 "d9": 1,
 "b9": 1
 },
 "t": 1391809957089
}

Donde:

  • “e” es el atributo que representa cada uno de las celdas sobre las que ha pasado el ratón.
  • “t” es la fecha en la que se ha insertado el registro (un simple new Date().getTime() en Javascript).

Cabe suponer por lo tanto, que usando la característica RESTful de CouchDB, podamos sacar un registro concreto de la base de datos mediante la llamda a esta URL: http://127.0.0.1:5984/dibujo/9181ffd10aa47c52e1cee65ce0006ecd . Esto es, usamos el campo _id para identificar el elemento, asique lo tenemos fácil a la hora de recuperar la información de la base de datos.

Por otra parte también parece interesante recuperar un listado de los elementos que hay en la base de datos, para poder cargar la información. En una base de datos tradicional NoSQL haríamos algo así como  SELECT * FROM dibujo. Pero esto es una base de datos NoSQL y se emplea otro paradigma: MapReduce . A grandes rasgos es una combinación de dos funciones, Map y Reduce, que se aplican de forma secuencial, una detrás de otra y que construyen a partir de un conjunto de datos un conjunto de resultados:

  • Map se encarga de recorrer todos los elementos de la base de datos y extraer como resultados los que cumplen el criterio indicado. Es decir, para un conjunto de datos almacenados en la base de datos extrae otro conjunto de resultados.
  • Reduce se aplica sobre los elementos resultados obtenidos de Map para obtener un valor reducido, como por ejemplo la suma de un determinado campo de los resultados. Gracias a la arquitectura interna de CouchDB basado en árboles-B (B-tree) se consiguen unos rendimientos espectaculares.

Vamos a crear una función Map que nos devuelva los elementos que en la base de datos usando Futon (http://127.0.0.1:5984/_utils/).

Acedemos a nuestra base de datos (dibujo) y a la derecha arriba seleccionamos en el desplegable “View” la opción “Temporary view…”. Aparecerá una tabla con dos grandes celdas: la de la izquierda para escribir la función de Map y la de la derecha para Reduce. Vamos a ir solamente con la de Map.

couch1

Si vemos la función por defecto es la siguiente:

function(doc) {
 emit(null, doc);
}

couch1

Esta función Javascript se aplica a todos y cada uno de los documentos de la base de datos. Por ello tiene un parámetro llamado “doc”, que representa cada documento. Sí, se llama una vez por cada documentos que exista. El siguiente elemento que destaca es la función “emit”, que se encarga de incluir un resultado. Este resultado se emite en forma de dupla: [key, value]. Los resultados se ordenan respecto a la key (recordad la estructura de árbol-B). Además debemos saber que esta función se aplica cuando hay un cambio en la base de datos, y no cada vez que se llama la vista. Esto es una gran ventaja de cara al rendimiento de la base de datos.

Nuestra función Map será la siguiente:

function(doc) {
if(doc.e && doc.t)
 emit(doc.t, doc.e);
}

Que no es otra cosa que emite un resultado cuando “e” (los elementos del grid y las veces que se ha pasado el ratón sobre ellos) y “t” (la fecha) no sean nulos. Además emite como resultado la dupla (t,e) , es decir, el tiempo o fecha en la que se ha creado el registro y el contenido. Pulsamos sobre el botón “Run” y vemos los resultados (es posible que no veas nada porque aún no tienes datos… en este caso crea varios documentos nuevos con New Document). Si los resultados son los esperados, salvamos pulsando el botón Save As. De nombre elegiremos “_design/dibujo” y de nombre de la vista será “todos”.

Si hemos hecho todo bien veremos cómo podemos acceder a esta vista mediante la URL: http://127.0.0.1:5984/dibujo/_design/dibujo/_view/todos

Es el turno ahora de modificar el ejemplo de AngularJS para poder escribir y leer los registros de la base de datos.

En primer lugar debemos saber que AngularJS dispone de una interfazz para hacer llamadas AJAX contra el servidor fácilmente, al estilo JQuery. Esta interfaz es $http (http://docs.angularjs.org/api/ng.$http) y está especialmente diseñada para soportar el protocolo REST.

Comenzaremos por modificar el controlador existente para añadir una nueva función que salve el estado actual del juego:

$scope.saveElementos = function()
{
    $http.post("http://127.0.0.1:5984/dibujo", {"e":$scope.elementos,"t":new Date().getTime()})
    .success(function(datos)
    {
       $scope.listadoElementos.push(datos);
       $scope.respuestaServidor = datos;
    });
}
 

Podemos ver cómo hacemos una petición de tipo POST a la URL de la base de datos, pasando por parámeotr un objeto que tiene dos atributos “e” para salvar los elementos y “t” para salvar la fecha y hora. Los elementos se pueden sacar de $scope.elementos, que como ya sabemos de artículos anteriores, está continuamente actualizado gracias al doble binding con la vista de angular. La fecha se saca simplemente los milisegundos con el new Date().

Como se trata de una función asíncrona, preparamos una función anónima con el resultado del servidor. Por una parte insertamos con push() el resultado (que es el elemento insertado) en un listado en el que tenemos todas las partidas guardadas. Por otra parte actualizamos una simple etiqueta que muestra por pantalla el resultado. No hemos implementado la función de error.

Así se fácil es. Se crea una función que usa $http y funciona correctamente. ¡Ah! Es buena idea incluir la inyección del elemento $http en el controlador que acabamos de modificar.

angular.module('angularJs2BbApp').controller("ctrlDibujo", ["$scope","$http",function($scope, $http, elementosProvider)

El último paso para salvar elementos es crear un botón o un enlace que llame a esta función. Esto se tiene que hacer en la vista:

<button ng-click="saveElementos()"class="btn btn-warning">Salva elementos...</button>

Cuando se haga click sobre este botón se llamará a la función y se creará un nuevo estado en la base de datos con los datos de las celdas del dibujo.

Problema CORS

Aún queda un problema por resolver… y es el CORS (Cross-Origin resource sharing). Que resuminéndolo en una frase sería una llamada AJAX hecha de un dominio a otro. Es decir, nuestra aplicación puede estar corriendo en 127.0.0.1:9000 si la ejecutamos con gruntJS, pero el servidor CouchDB y su interfaz RESTful responden en 127.0.0.1:5984. Aunque es la misma IP no nos debemos olvidar del puerto, que hace que sea otro dominio diferente, y por eso dé el error. ¿Cómo podemos resolver esto?

La solución pasa por hacer que el servidor CouchDB emita una cabecera HTTP en cada llamada, indicando que admite que le hagan peticiones desde determinados dominios. Esta cabecera es “Access-Control-Allow-Origin”. ¿Cómo configuramos CouchDB par que la emita?

Es muy sencillo, sólo dos pasos (asegurante de tener una versión que lo soporta, la 1.5.0 sí que lo soporta – desconozco a partir de qué versión funciona):

1. ir a /etc/local.ini
2. Introducir estos valores:

[httpd]
enable_cors = true
[cors]
origins = *

3. Reinicamos el servidor CouchDB

Con esto le indicamos al servidor de CouchDB que vamos a recibir peticiones AJAX desde otro dominio, y que las aceptamos. Nuestra aplicación Angular, al recibir la contestación, verá que en la cabecera se permite su dominio, esto es “Access-Control-Allow-Origin:http://127.0.0.1:9000&#8221;. Así debería funcionar todo correctamente.

Además de ir almacenando las partidas, parece interesante disponer de algún método para ver las partidas que ya hay almacenadas. Es en este momento cuando vamos a utilizar la vista creada unos párrafos atrás. Cada vez que se cargue la aplicación web mostraremos un listado con las partidas guardadas en la base de datos.

Para ello procedemos de modo parecido a antes, utilizando la interfaz que nos proporciona $http para hacer llamadas AJAX:

$scope.loadTodosElementos = function()
{
 $http.get("http://127.0.0.1:5984/dibujo/_design/dibujo/_view/todos").success(function(datos)
 {
 $scope.listadoElementos=datos.rows;
 });
}

La URL que invocamos es la de la vista creada, que se encuentra dentro del documento_design dibujo. Esta vista nos devuelve un objeto JSON con metainformación, y dentro de su campo “row” se encuentra el listado de resultados que necesitamos. Utilizamos la variable “listadoElementos” para guardar cada uno de los estados del juego.

Usamos AngularJS para mostrar todos los elementos con un simple ng-repeat

<ol>
    <li ng-repeat="lista in listadoElementos"><a ng-click="loadElementosById(lista.id)" >{{formateaFecha(lista.key)}}</a></li>
</ol>

Si te fijas, para mostrar la fecha hemos usando la función “fromateaFecha”, que también es una función que forma partel del controlador que hemos creado. Vamos a crearla:

$scope.formateaFecha = function(milis)
{
    console.log("fecha: " + milis);
    return moment(new Date(milis)).format('llll');
}

Para facilitar el trabajo se ha usado http://momentjs.com/ que es una biblioteca de Javascript para el manejor de fechas. La puedes bajar directamente e incluirla dentro del index.html o usar bower si has usado Yeoman:

bower install -S momentjs

En el listado de las partidas, si te fijas, he incluído un enlace con ng-click y la llamada a la carga de la función por Id. Puedes suponer que se trata de otra función que hace uso del API RESTful de CouchDB:

$scope.loadElementosById = function(id)
{
    $http.get("http://127.0.0.1:5984/dibujo/"+id).success(function(datos)
    {
        $scope.elementos = datos.e;
    });
}

Con la URL apuntando hacia el ID correspondientes descargaremos el estado del dibujo determinado y como estamos usando AngularJS, al cambiar el modelo (la variable $scope.elementos), se cambia rápidamente la vista (que es el grid de divs sobre los que se pasea el ratón). El resultado total sería algo como esto:

angular2

En resumen, hemos visto cómo usar $http para conectar con una base de datos CouchDB que pone a nuestra disposición una interfaz RESTful para el desarrollo de aplicaciones AngularJS de una forma rápida y escalable. 

PS: podéis acceder al GitHub de esta aplicación en: https://github.com/4lberto/AngularJS_CouchDB

Creando una sencilla aplicación con AngularJS y Yeoman

(Acceso directo a la demo: http://rusizate.com/angular )

Las páginas Webs ya no son lo que eran. Estamos pasando del modelo XHTML+CSS en donde el servidor consideraba que el cliente era un poco “tonto” y le enviaba el código fuente para representarlo sin que tuviera mucho que pensar (salvo unos pequeños destellos de Javascript), a un modelo donde gran parte de la aplicación reside en cliente (en s unavegador, mejor dicho), dejando al servidor como poco más que un interfaz para el acceso a base de datos. Esto es lo que nos trae HTML5 y la gran evolución de Javascript: clientes potentes en Javascript que se comunicación con el servidor a través de AJAX.

Este fenómeno se ha llamado las Webapps Single-page application. Desde el punto de vista tradicional del HTML, el cliente accede a una sola página (o unas pocas), done se descargan las librerías y código Javascript una sola vez. A partir de ese momento, todos los cambios se realizan mediante manipulación del DOM de esa única página, sin tener que navegar por otras páginas. La comunicación con el servidor se hace por llamadas AJAX en el protocolo preferido.

Es fácil suponer que, al pasar la responsabilidad desde el servidor al cliente, la complejidad y tamaño del código Javascript aumenta de forma exponencial. Como sabemos, es mucho más fácil hacer un buen código estructurado en Java (sobre todo gracias a sus múltiples frameworks – struts, spring, google juice…) que hacerlo en Javascript. Surge por lo tanto la necesidad de tener algún tipo de framework que ayude a estructurar las aplicaciones en Javascript que corren en el cliente. Afortunadamente muchos proyectos open source se han puesto las pilas como por ejemplo EmberJS, Backbone, Knockout o AngularJS como los más destacados.

Estos frameworks implementan están basados en implementar el Modelo Vista Controlador (MVC) y se encargan de gestionar de forma automática las dependencias entre los componentes, ahorrando mucho trabajo al programador (y muchos errores).

En este post nos vamos a centrar en el que está pegando con más fuerza actualmente, que no es otro que AngularJS, desarrollado por Google. Vamos a ver los pasos necesarios para crear una aplicación desde cero. La gran actividad de desarrollo de Angular incluye la adaptación de herramientas para la generación de las aplicaciones Web para el soporte de este framework. La herramienta que vamos a usar en esta ocasión es Yeoman , que nos permitirá partir de una aplicación básica con Angular para desarrollar la nuestra, así como a través de Grunt, poder disponer de un servidor con recarga automática y poder generar una versión e distribución.

Comencemos…

Para que funcione Yeoman en nuestro entorno de desarrollo necesitamos instalar NodeJS y NPM (Node Package Manager). Tal y como aparece en las instrucciones de la página oficial de Yeoman, tenemos que instalarlo de modo global:

npm install -g yo

(-g indica que es un paquete global)

Yeoman utiliza generadores para crear las aplicaciones web, que no es otra cosa que plantillas para generar aplicaciones con unos componentes determinados. Se podría decir que es como los archetypes de Maven. Existen muchos de ellos, por ejemplo el de una aplicación Web simple con una página de demo es “npm install -g generator-webapp”. Para angular vamos a usar:

npm install -g generator-angular


Para que todo funcione correctamente debemos tener instalado
GIT
 

Una vez tenemos el generador de angular cargado en NPM, podemos usar yeoman para generar la aplicación: creamos un directorio en nuestro disco, en donde residirán todos los ficheros y directorios, y accedemos a él. Una vez dentro ejecutamos:

yo angular

Nos pedirá configurar algunas dependencias, como por ejemplo el uso de Twitter Bootstrap (que sí usaremos).

Esperamos pacientemente a que se descarguen todos los ficheros (puede llevar un buen rato). Al final tendremos un árbol de directorio con ficheros de configuración en el raíz. Destacan especialmente la configuración de Bower (bower.json) y de Grunt (Gruntfile.js). Servirán para introducir nuevas dependencias y configurar las tareas del servidor, tales como mostrar el servidor, crear la versión de distribución, etcétera. Serán de gran utilidad para el desarrollo.

Una vez está todo perfectamente descargado iniciamos el servidor de desarrollo utilizando Grunt. Este servidor tiene la utilidad de que está al corriente de los cambios en los ficheros, de modo que cuando editando cualquier js, css o html y lo salvamos, el servidor es capaz de refrescar de forma automática el navegador para que se vean reflejados los cambios (podemos ver el parámetro watch del Gruntfile.js). Lo lanzamos en modo servidor, en el directorio raíz de la aplicación:

grunt serve

Si todo ha ido bien (a veces hay que usar el parámetro grunt serve –force), se abrirá nuestro navegador predeterminado con la página de inicio del proyecto que se ha generado al hacer uso de Yeoman. Es hora de comenzar la edición.

Dentro del directorio creado, podemos localizar el directorio “apps”, dentro de el cual está la aplicación propiamente dicha. El resto pertenecen a ficheros de configuración o dependencias de Bower y otras tareas. Los ficheros que vamos a tocar serán los siguientes:

  • index.html que contiene la plantilla donde se cargarán las vistas de la Webapp. Esta plantilla contiene un tag ng-view donde en su interior se insertarán las vistas.
  • scripts/app.js que es donde se define el módulo principal de la aplicación, y donde además se establece la definición del enrutamiento, asignando a cada patrón de URL del navegador, una vista que mostrar y un controlador para esa vista. Hay que tener en cuenta que se trata de una aplicación web en una sola página, por lo que los cambios de vista son realmente cambios de capas y no de páginas.
  • scripts/controllers/main.js que es el fichero donde vamos a definir el controlador principal, y por tanto, en esta pequeña demo, vamos a establecer el modelo (los datos) y las funciones básicas.
  • views/dibujo.html que es la vista que será cargada dentro de la plantilla (index.html) y será cargada porque se ha asignado en scripts/apps.js como ruta básica con el controlador determinado.

Con estos 4 elementos hemos definido la vista (la plantilla y la vista); el modelo (dentro del código del controlador); y el controlador (que define las acciones sobre el modelo). Todo ello relacionado con el sistema de enrutamiento.

Vamos a ver la aplicación en detalle:

Se trata de una sencilla aplicación que define una malla o grid de elementos (una tabla…) en los que al pasar el ratón por cada una de las celdas toma un color cada vez más oscuro. En realidad, cada una de las celdas está asignada a un atributo de un objeto (que imita una malla para guardar los datos). Existe un binding o relación entre cada celda y la propiedad indicada, de modo que al pasar el ratón por encima de la celda, el atributo del modelo correspondiente a esa celda aumenta su valor en una unidad (es un número). Al mismo tiempo, cada celda refleja con su color el valor que tiene el atributo relacionado. Cuanto mayor es el número más oscura será la celda.

Supone un ejemplo muy visual, sobre todo si se examina el código, de cómo AngularJS es capaz de relacionar la vista y el modelo y cómo reacciona en tiempo real a los cambios del modelo. Si paso el ratón por encima cambia el modelo, y al cambiar el modelo se cambia la vista de forma instantánea y casi sin hacer nada en el código. ¡Es algo mágico!

¿Qué se ha cambiado respecto de la aplicación base?

En primer lugar vamos a ir con el fichero “index.html”. Este fichero es la plantilla básica, que si hemos seguido las instrucciones propuestas nos debería valer. Tiene todo lo necesario para que funcione AngluarJS y además tiene Twitter Bootstrap 3. Lo que hay que destacar de este fichero es el siguiente tag:

<div class="container" ng-view=""></div>

Dentro de este tag, al tener el atributo ng-view, se van a cargar las vistas que definamos en la aplicación. Una vista es un fragmento de código HTML con notación de AngularJS que va a ir dentro del tag con ng-view

El siguiente elemento que vamos a definir es la aplicación, en el fichero scripts/app.js

'use strict';

'use strict';

angular.module('angularJs2BbApp', [
'ngCookies',
'ngResource',
     'ngSanitize',
     'ngRoute'
 ])
.config(function ($routeProvider) {
     $routeProvider
         .when('/', {
             templateUrl: 'views/dibujo.html',
             controller: 'ctrlDibujo'
 })
 .otherwise({
     redirectTo: '/'
 });
});

Aquí se pueden ver dos elementos importantes.

En primero lugar tenemos la declaración del módulo ‘angularJs2BbApp’ que es el nombre que le hedado a la aplicación. Como parámetros tenemos un array con las dependencias qur utiliza, entre ellas ngRoute para el enrutamiento. Cuando definamos controladores, servicios u otros elementos, veremos que se relacionan con el módulo. Es muy importante por lo tanto el nombre del módulo, ya que haremos constantemente referencia a él para decorarlo con nuevos elementos.

Por otra parte tenemos la configuración de un routerProvider en la aplicación. Es una función anónima que recibe la referencia al routeProvider general (comienza por $), y establece las URL a las cuáles responde la aplicación. Es un sistema que imita al funcionamiento habitual de una página web: cuando cambia la URL se carga otra página. Aquí, al tratarse de una WebApp solamente se utiliza una página (la del index.html), pero se van cambiando los contenidos de las vistas. El usuario creerá que está cambiando de página, pero realmente solo cambian los contenidos de algunas capas. Por ejemplo, una ruta puede ser el raíz “/” o mejor dicho “#/” y otra ruta puede ser “#/dibujo”. En el ejemplo se puede ver cómo para la ruta raíz, se mostrará la plantilla “dibujo.html” y usará el controlador llamado “ctrlDibujo”.

En el enrutamiento se indica tanto el templateURL o vista (view), que como vemos corresponde a un fichero html (que dentro tendrá sintaxis de AngularJS), y un controlador, que está definido en el fichero main.js. Es una buena forma de separar la funcionalidad y las vistas de nuestra aplicación Web de una sola página pero muchas pantallas.

El siguiente elemento será el Controlador, que está situado en el fichero main.js:

'use strict';

angular.module('angularJs2BbApp').controller("ctrlDibujo", function($scope)
 {
     $scope.alto = ['a','b','c','d','e','f','g','h', 'i', 'j', 'k', 'l', 'm', 'n'];
     $scope.ancho = [1,2,3,4,5,6,7,8,9,10,11,12];
     $scope.actual = "nulo";
     $scope.elementos = {};

$scope.activaElemento = function(indice1, indice2)
     {
         var cadena = indice1.toString()+indice2.toString();
         $scope.actual = cadena;

if($scope.elementos[cadena]==null)
             $scope.elementos[cadena]=1;
         else
             $scope.elementos[cadena]++;
     }

});

Volvemos a ver de nuevo la invocación al módulo, que es llamado con el mismo nombre. Esto lo que hace es recuperar la referencia al módulo para decorarlo con nuevas características. En este caso va a introducir un controlador llamado “ctrlDibujo” (recordar que en el fichero app.js hemos definido que es el controlador para la ruta y la vista dibujo.html).

El controlador, como todos sabemos (o deberíamos), se encarga de manipular el modelo, de manera que está formado principalmente por funciones. En este caso está definido como una función anónima que recibe el $scope, que no es otra cosa que la referencia al $scope del módulo. El $scope del módulo nos da acceso a toda la información del modelo. Si se quiere acceder a un atributo o función que tiene que estar disponible para el modelo se hace a través de $scope.elemento.

Si vemos el código podemos observar la definción de 2 vectores (alto y ancho); una cadena de texto (actual) y un objeto vacío llamado elementos. Estas tres variables están perfectamente disponibles para cualquier elemento del módulo, así como para la vista y otros elementos más avanzados que no vamos a definir en este post. Obviamente estos 4 elementos pueden ser considerados como el Modelo de la aplicación, puesto que almacenan la información del estado y lo que queremos mostrar por pantalla y ser manipulado. Este modelo ha sido definido dentro del controlador por cuestiones de simplicidad ya que este post es una pequeña introducción. En otras condiciones se utilizan “servicios” para una comunicación con un servidor a través de REST, por ejemplo.

Además de estas cuatro variables existe una función que constituye el verdadero controlador de la aplicación. Esta función se encara de dos aspectos. Por una parte establece el valor actual como el nombre del cuadrado sobre el que está pasando el ratón. Cada cuadrado estará designado por el varlo correspondiente de alto y ancho, es decir, sus coordenadas que son sacadas de la combinación de las variables alto y ancho. Un ejemplo sería el primer elemento “a1” o “c6” por ejemplo. Este valor se usará para mostrar por pantalla cuál es el elemento actual. Por otra parte lo que hace es tomar el objeto elemento y añadirle como propiedad la coordenada, además del número de veces que se ha pasado el ratón por encima. La primera vez, será 1 y se creará en ese momento el miembro del objeto. Esta función será invocada en un evento mouse over como veremos en la vista.

Con esto hemos creado el controlador, además de una parte del modelo. Ya solamente nos queda por definir la vista, que está en el fichero “dibujo.html”

<div class="row">
     <div class="col-xs-12 text-center">
         <h1>Draw!</h1>
     </div>
</div>
<div class="row">
     <div class="col-xs-12 text-center">
             Last selected: {{actual}}<br />
         </div>
</div>
<div class="row">
     <div class="col-xs-12" id="areaDibujo">
         <div class="row" ng-repeat="al in alto">
             <div class="col-xs-1 cursorPointer cuadrado text-center" ng-repeat="an in ancho" ng-mouseover="activaElemento(al,an)" ng-style="{'background-color':'rgba(0,0,0,'+(elementos[al+an]/20)}">
                     {{elementos[al+an]}}
             </div>
</div>
     </div>
</div>

Si no estás familiarizado con Twitter Bootstrap puedes imaginar que las clases “row” o “col-xs-…” son para maquetar como si fueran tablas (dicho muy genéricamente). Por lo demás parece un código HTML bastante estándar. Acuérdate que esta vista se carga dentro de aquel div con el atributo ng-view que estaba en index.html. Por eso no tiene ni body ni head ni nada de lo demás…

En una revisión descendente podemos ver que el primer elemento que llama la atención es {{actual}} en medio del texto (sí, con llaves dobles). Esto indica que se pinte ahí el valor de “actual”. Si recuerdas, en el controlador se ha definido la variable $scope.actual. Es evidente que es la misma variable. Además si recueras, la función “activaElemento” hacía que esta variable cambiase.

Es aquí donde comienza la magia de AngularJS frente a métodos tradicionales como por ejemplo jQuery. AngularJS está vigilando todo el rato los cambios en el modelo (la variable), de modo que ante cualquer cambio de la variable $scope.actual, actualiza el valor {{actual}} por el nuevo valor, sin que tengamos que hacer nada. Esto equivaldría a hacer un jQuery(“#elemento”).html() cada vez que un controlador alterase el valor de $scope.actual. El funcionamiento es casi mágico. ¿Y cuando cambia el valor de “actual”? Pues cambia cuando álguien invoque la función “activaElemento”.

El siguiente paso si seguimos para abajo es el de ng-repeat=”al in alto”. Si recuerdas $scope.alto es un vector con elementos literales de la a a la letra n. Mediante ng-repeat=”al in alto” lo que hace angular es repetir el tag que lo contiene tantas veces como elementos tenga alto, y además, a cada vez le asigna ese elemnto a la variable “al”. Como vamos a hacer un tablero (2 dimensiones), repetimos instrucción en el siguiente tag, con ng-repeat=”an in ancho”. Así tendremos alto*ancho elementos y encima podremos usar las variables “al” y “an” para etiquetar cada elemento. Como sucedía con “actual”, vamos a encerrar el valor de elementos[al+an] dentro de unas llaves dobles para que se muestren por pantallas. Con esto hacemos que cada “div” tenga en su interior un texto que corresponde al número de veces que ese elemento ha sido atravesado por el cursos. Es fácil de entender: tenemos el objeto “elementos” vacío. Si pasamos el ratón, la función toma los valores de al (“a”) y an (3) y los une haciendo un literal (“a3”), que se asigna como miembro del objeto elementos elementos.a3=1 ó elementos.a3++

Vamos ahora con las fuciones. Tenemos dos atributos, uno es ng-mouseover, que llama a una función cuando se pasa el ratón por encima del elemento. En este caso tenemos la función controladora que no es otra que “activaElemento”. Recibe por parámetro las coordenadas del elemento, y como se puede ver en el texto, asigna ese valor a “actual” (lo que provoca el cambio automático gracias a la magia de AngularJS en el texto que acomapaña a Last selected), y por otra parte añade o aumenta en una unidad el valor del miembro elementos.al+an. Entonces el controlador va cambiando los valores de elementos, y por tanto va mutando todo el ratola vista gracias al doble “binding” entre vista y modelo.

Finalmente hay un último atributo, llamado ng-style que modifica el color de fondo de la capa, estableciendo que sea un color negro (0,0,0) con una transparencia que dependen del valor de veces que haya pasado ratón. Si ha pasado una vez será 0.05. y si ha pasado 10 veces será 0.5 y 20 veces 1 (es decir, opacidad total). Se ha tomado 20 como un valor arbitrario, por eso se divide por 20. Como se puede suponer, también hay un binding aquí con el valor de elementos[al+an] (ojo! recuerda que elementos[al+an] es lo mismo que decir elementos.al+an, aunque esta última es un error de sintaxis, por eso se usa la notación corchetes al tener una suma). Vamos, que si cambia elementos[al+an] porque se activa el controlador porque álguien pasa con el ratón por encima, se actualiza la vista de forma mágica.

En resumen, hemos visto cómo AngularJS nos brinda un framework para el desarrollo de aplicaciones Javascript en cliente muy potentes de forma rápida y estructurada. Se encarga de implementar un verdadero patrón MVC de forma muy eficiente para el programador, con pocas líneas de código. Existen muchas otras estructuras y elementos para hacer aplicaciones mucho más complejas y más útiles.

Si queréis probarlo por vuestra cuenta he dejado la demo funcionando en http://rusizate.com/angular . Podéis encontrar el repositorio con el código para que podáis extenderlo en: https://github.com/4lberto/AngularJSDemo1