Páginas

viernes, 26 de abril de 2013

Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry

Backbone.js
Normalmente escribo artículos con ejemplos que tratan sobre una única tecnología, al tratar cada tecnología por separado entender que puede ofrecer es más sencillo pero en una aplicación real normalmente se usan varias tecnologías combinadas y al mismo tiempo. Esta entrada aunque sigue siendo un ejemplo es mucho más parecido a lo que podría ser una aplicación real que lo que he explicado durante las últimas entradas. En esta serie sobre javascript he explicado un montón de cosas por separado entre ellas RequireJSMustacheBackbone, servicios rest con Tapestry y RESTEasy. En esta entrada haré un ejemplo haciendo uso de todas las anteriores y alguna cosa más adicional como twitter bootstrap.

El ejemplo consiste en una lista de tareas, pudiéndose introducir nuevas tareas y marcarlas como realizadas. También se podrá eliminar de la lista las tareas completadas y ver un resumen con el número de tareas completadas y de tareas totales.

En la parte cliente de la aplicación se hace uso de RequireJS para manejar la carga de los archivos javascript necesarios, de backbone para gestionar la lista de tareas y realizar la comunicación con el servicio REST del servidor y de Mustache como motor de plantillas para generar la vista (el html). En la parte del servidor usa Tapestry como framework web y RESTEasy como librería para implementar un servicio REST que proporcionará la persistencia en memoria de la lista de tareas.

Backbone permite organizar el código aplicando el patrón de diseño MVC (modelo-vista-controlador) ampliamente extendido en los frameworks web. Este patrón de diseño tiene la ventaja que separa la aplicación en tres partes diferenciadas:
  • El modelo: que contiene los datos del dominio que maneja la aplicación que cuando es modificado lanza eventos para que el controlador y la vista actúen en consecuencia.
  • El controlador: que reacciona ante los eventos que se produzcan en el modelo o por el usuario y modifica adecuadamente el modelo o la vista según el evento.
  • La vista: que a partir del modelo produce la interfaz que se ofrece al usuario para que pueda manipularlos, los eventos producidos en la vista son manejados por el controlador.
Como se ve las tres partes se relacionan entre si pero están separadas de forma que cada una se encargue de una tarea. El código queda bien organizado y se evita el código espagueti que podríamos obtener de otra forma.

En una aplicación con la combinación de Backbone, Mustache y un servicio REST el servidor devuelve el html de la página inicial, las plantillas y los datos del servidor y se delega en el navegador del usuario la tarea de renderizar el html final. Esto tiene la ventaja de que entre el navegador y el servidor viajan menos datos (los datos y las plantillas tendrán menos tamaño que los datos formateados a html) y la parte del servidor se simplifica (no es necesaria la lógica para formatear a html los datos que dependiendo del framework hacen los jsp, tml de tapestry o gsp de grails). Aunque el código de la parte cliente crecerá, el código de la parte del servidor se hará más simple. Una vez cargada la página inicial entre el servidor y el cliente solo viajan los datos en formato json.

Veamos el ejemplo a continuación empezando por el modelo. El modelo con Backbone se define con Backbone.Model.extend, donde podremos indicar las propiedades de los objetos, también podemos definir funciones de utilidad. En este caso el modelo estará formado por los objetos Tarea que tendrán las siguientes propiedades:
  • Un id que identificará la tarea.
  • La descripción de la misma.
  • Y un indicador de si está completada.
Pero en la lista de tareas no solo tendremos un objeto Tarea, tendremos una colección de ellas. Esta colección también forma parte del modelo y se define con Backbone.Collection.extend, los elementos de esta colección serán objetos del modelo Tarea y lo indicamos en la propiedad model, también podemos definir funciones de utilidad como una función para obtener un array con las tareas completadas y otra para eliminar las tareas completadas que nos interesarán para ofrecer la funcionalidad de la lista de tareas. Las propiedades urlRoot y url son importantes para que backbone sepa localizar el servicio REST que proporcionará la persistencia.

La interfaz del servicio web REST es:

En Backbone la vista y el controlador de define en una misma entidad con Backbone.View extend. Aunque se definen ambas cosas a la vez la vista principalmente está formada por el método render, el resto será parte del controlador. Otra sección importante de la vista es la propiedad events donde indicaremos que eventos manejará el controlador y sobre que elementos de la vista, básicamente se define como clave el evento (click, change, ...) y un selector similar a los usados en jquery y como valor el nombre de la función que manejará el evento. En la vista también podremos definir métodos de utilidad. Tendremos dos vistas: una para para una tarea y otra para la aplicación de la lista de tareas.

En el método render se hace uso de Mustache para generar la vista a partir de una plantilla que contiene el código html, la plantilla combinado con los datos del modelo genera un resultado que será incluido en el html de la página. En las funciones initialize se inicializa la vista principalmente para definir que eventos que produzca el modelo serán tratados por el controlador, estos son las funciones on sobre el modelo, por ejemplo, nos interesará saber que se añade, modifica o elimina una tarea de la colección de tareas para actualizar la vista adecuadamente.

En el caso de TareasApp en el método initialize además se inserta en el elemento «el» que se le pasa en el constructor la vista inicial de la aplicación. De esta forma podríamos crear varias instancias de TareasApp.

Las plantillas de Mustache las he definido entre elementos <![CDATA[ ... ]]> ya que las plantillas de Tapestry han de ser xhtml válido, algunas expansiones de Mustache como {{attrs.checked}} de la plantilla tarea-template Tapestry no las entiende como válidas ya que realmente la plantilla Mustache en este caso no es html válido.

Una vez desarrollada la parte del servicio REST en el servidor con RESTEasy integrarlo con Backbone es muy sencillo, únicamente deberemos llamar en los puntos adecuados a las funciones save y destroy de los modelos. Al método save se le llama cuando se añade una nueva tarea en TareasApp.addTarea y cuando se modifica en TareaView.onChangeCompletada. Cuando se hace clic en el botón para limpiar las tareas completadas se llama a la función destroy de cada uno de los modelos. Estos métodos lanzarán las peticiones AJAX al servicio REST de forma automática variando su método y con su correspondiente url según hemos definido con las anotaciones del servicio REST: POST (crear), PUT (actualizar) y DELETE (eliminar).

Petición al crear una nueva tarea
Petición al marcar como completada una tarea
Petición al limpiar tareas completadas
La carga de inicial de la lista de tareas puede hacerse de dos formas: devolviendo los datos de las tareas cuando se pide la página o una vez cargada la página mediante una petición AJAX adicional. En Backbone recomiendan la primera ya que así se evitan peticiones al servidor innecesarias. Además aunque hagamos que las peticiones AJAX sean rápidas probablemente sean lo suficientemente lentas para que el usuario vea como se va rellenando el contenido de la página y dependiendo del html verá como los elementos van cambiando de posición lo cual puede ser molesto. Aunque hay que decir que incluir los datos en la propia página impediría cachear la página. Al final de la página Index.tml, está la sección para cargar la lista inicial de tareas que se pasará a TareasApp después de su constructor en el método resetTareas.

Esta es una captura del ejemplo:

Después de haber usado Backbone en este ejemplo simplemente tengo que decir que es una gran herramienta y que facilita y ayuda a organizar el código javascript en gran medida. Permite separar los datos de la aplicación que forman el modelo del controlador y la vista que reaccionan mediante los eventos producidos en el modelo. También permite desarrollar aplicaciones con un gran peso de javascript en la parte cliente sin que el código se convierta posteriormente en un infierno de mantenimiento aún con la ayuda de jQuery. Es una ayuda tan grande para hacer algunas cosas de la interfaz del cliente como lo es jQuery para manipular los elementos html.

Algunas partes no las he explicado como las plantillas de Mustache, el uso de RequireJS o la parte del servidor del servicio RESTEasy ya que ya lo que he hecho en entradas anteriores que puedes visitarlas mediante sus enlaces.

Como en el resto de entradas el código fuente completo lo puedes encontrar en mi repositorio de GitHub. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip con el anterior enlace.

En la siguiente entrada puedes ver el mismo ejemplo pero implementado con Marionette que simplifica algunas cosas respecto a Backbone. Además en el ejemplo de Marionette puedes ver como conseguir internacionalización y como externalizar las plantillas de Mustache del html.

Referencia:
Introducción y ejemplo de RequireJS
Introducción y ejemplo de Mustache
Logging en Javascript con log4javascript
Capturar errores de Javascript
Optimizar módulos de RequireJS y archivos Javascript
Patrón de diseño MVC del lado cliente con Backbone.js

viernes, 19 de abril de 2013

Introducción y ejemplo de Backbone.js

Backbone.js
Las aplicaciones y páginas web están haciendo uso de código javascript cada vez en mayor cantidad para proporcionar aplicaciones con funcionalidades más complejas. En estos momentos las librerías para utilizarse en javascript son muchas y una aplicación puede hacer uso de varias de ellas.

A pesar de las facilidades que proporcionan librerías como jQuery para manipular los elementos de una página web de forma dinámica, mucho código mal estructurado puede llegar a convertirse en un perfecto código espagueti, aunque jQuery facilita y simplifica muchas tareas en el manejo de los elementos de una página no es suficiente, a pesar de jQuery se puede llegar a tener un código con un mar de selectores, mantener sincronizados con la interfaz con el estado de la aplicación y la base de datos puede convertirse en una tarea complicada dar como resultado código con el que apetece poco trabajar.

Backbone.js trata de de resolver algunos de estos problemas proporcionando un marco sobre que el que organizar el código. Backbone.js en esencia en una librería modelo-vista-controlador (MVC) para javascript. Basándose en este patrón de diseño cada parte es independiente del resto. El modelo conserva el estado de la aplicación y produce eventos al ser modificado de forma que la vista se pueda actualizar de una forma independiente para ambos, la vista proporciona la interfaz del controlador a través del que se modifica el modelo de forma adecuada, todos los elementos están relacionados pero cada uno se encarga de una parte.

Patrón de diseño Modelo Vista Controlador (MVC)

Backbone además proporciona una forma para trabajar con servicios REST tanto para recuperar los datos de los modelos como para actualizarlos, guardarlos y eliminarlos en el servidor. A continuación pondré un pequeño ejemplo usando backbone.js que calcula el área de un rectángulo dado su alto y ancho y que lo muestra en pantalla. Como backbone.js sigue el patrón de diseño MVC por un lado tendremos el modelo, en nuestro caso el rectángulo con las propiedades alto y ancho que nos servirán para calcular el área, por el otro la vista que se actualizará según los datos del modelo introducidos en dos campos de texto, finalmente el controlador que captura los eventos como los cambios en los campos de texto, modifica el modelo y el modelo notifica a la vista para que se actualice.

Vemos un ejemplo simple y que muestra parte del potencial de backbone.js. El modelo se define con el siguiente código, además de las propiedades se puede definir funciones que nos puede ser de utilidad, como calcular el área o obtener un objeto para ser utilizado con Mustache:


La otra parte es la vista y el controlador, ambas cosas se definen con Backbone.View.extend:

La inicialización del ejemplo:


Las partes de esta vista-controlador son:
  • el: el elemento html sobre el que actuará la vista.
  • events: los eventos que manejará el controlador, es un mapa donde la clave es un selector muy parecido a jquery y el nombre de la función a llamar cuando se produzca.
  • initialize: con la función de underscore _.bindAll haremos que la referencia a this sea la vista en las funciones indicadas (render, onChangeAlto, onChangeAncho). A continuación se crea el modelo y cuando se produzca algún cambio en él (evento change) se actualice la vista. Finalmente, cuando se inicia la aplicación se dibuja por primera vez la vista.
  • onChangeAlto y onChangeAncho: obtienen el alto o ancho de los campos de texto y modifican el modelo.
  • render: se encarga de actualizar la vista cuando haya algún cambio en el modelo tal y como hemos echo en la función initialize con la función on sobre el modelo. Actualiza los inputs de alto y ancho con los valores del modelo y mediante una plantilla de Mustache forma el mensaje que se visualiza en el  elemento con id resultado.
Finalmente, queda el pequeño código html de la aplicación que contiene los campos de texto para introducir el alto y ancho y el elemento div donde se mostrará el área calculado del rectángulo:



Backbone.js no es la única librería que implementa el patrón MVC en javascript otras alternativas son knockout y AngularJS aunque esta últimas tienen la notable diferencia de que se basan en añadir atributos especiales a las etiquetas html para definir el modelo y asociar los eventos. Backbone.js no necesita añadir esos atributos especiales a la etiquetas html o «instrumentalizarlo», esto se hace a través de javascript lo que da una mayor separación entre el código html y el código javascript. Por ahí se dice que usando backbone.js para realizar lo mismo se necesitan más lineas de javascript pero hay que tener en cuenta que a cambio el html necesario es mucho más simple que en knockout o AngularJS lo que facilita que el trabajo con motores de plantillas como Mustache o Handlebars también sea más simple.

Este pequeño manual de backbone explica muy bien que es. Y el siguiente vídeo es muy interesante.


Como en el resto de entradas sobre esta serie de javascript el código fuente del ejemplo completo lo puedes encontrar en mi repositorio de GitHub. Este ejemplo se basa en RequireJS y Mustache visto en anteriores entradas que puedes consultar en el apartado de referencia al final de la entrada.

En la siguiente entrada mostraré un ejemplo más completo y usando muchas cosas al mismo tiempo de las que he estado hablando individualmente en las últimas entradas (ver referencia), será un ejemplo de una aplicación de lista de tareas por hacer que permite crearlas y completarlas, usará RequireJS, Mustache y Backbone (usándolo de forma más avanzada y completa de lo visto en esta entrada) en la parte cliente y Tapestry y RESTEasy en la parte servidor.

Referencia:
Introducción y ejemplo de RequireJS
Introducción y ejemplo de Mustache
Logging en Javascript con log4javascript
Capturar errores de Javascript

viernes, 12 de abril de 2013

Optimizar módulos de RequireJS y archivos Javascript

Una vez que tenemos desarrollada una aplicación que hace uso de RequireJS como herramienta para gestionar las dependencias y archivos de javascript podemos realizar una optimización para conseguir que haya menos archivos y tengan menor tamaño. Esta optimización hará que la aplicación web realice menos peticiones al servidor y necesite transferir menos bytes dando como resultado una aplicación más rápida.

Para conseguir esta optimización necesitamos tener instalado la herramienta node.js y descargar el javascript que realizará la optimización, r.js. En Arch Linux instalar node.js consiste en instalar su paquete con:

Teniendo instalado node.js y habiendo descargado r.js el proceso consiste en ejecutar un comando desde la terminal. Este es:

El contenido del archivo build.js contendrá alguna información como el archivo de salida y la localización del módulo principal de la aplicación, básicamente la configuración es:

Este sería el resultado de optimizar el ejemplo de introducción sobre RequireJS. En el resultado se han eliminado los espacios y tabuladores, los diferentes archivos se han fusionado en uno solo (en este caso únicamente figuras.js y main.js) y los nombres de las variables se han acortado, esto reduce el tamaño del Javascript final. Esta optimización sirve también en cierta forma como ofuscación del código si queremos dificultar a alguien (como a la competencia de un producto) se aproveche del código que hemos desarrollado:


Al optimizar los archivos es recomendable ver las notas que indican en la documentación de RequireJS.

Si se quiere optimizar archivos individuales de javascript o no usamos RequireJS se puede utilizar la herramienta Closure Compiler, es muy sencilla, no hace falta instalar nada en nuestro equipo sino simplemente usar el servicio y da un resumen de la optimización que realiza.

Este ejemplo de introducción a RequireJS es muy sencillo y no muestra del todo el potencial que podemos conseguir con la utilidad r.js. La optimización de la lista de tareas con Marionette, otro ejemplo que hice, es más bastante más complejo y en el puede verse la mejora que podemos conseguir en cuanto a número de peticiones necesarias a realizar al servidor, tamaño en kilobytes del código descargado y tiempo en cargar de la página.

Referencia:
Introducción y ejemplo de RequireJS
Introducción y ejemplo de Mustache
Logging en Javascript con log4javascript
Capturar errores de Javascript

viernes, 5 de abril de 2013

Capturar errores de Javascript

Tener un sistema de logging para el código Javascript de una página está muy bien para depurar el código cuando las cosas van mal en el sentido de que no hacen o no se comportan como se espera. Pero a veces las cosas pueden ir peor que mal, esto es, si hay algún error de «compilación» en en código de javascript el navegador detiene la ejecución y por tanto dejan de emitirse las trazas. Si el error nos sucede en el entorno de desarrollo o pruebas en nuestro propio navegador nos daremos cuenta del error pero en un entorno de producción no ya que el código se ejecuta en el navegador del usuario.

Sin embargo, no todo está perdido, los navegadores proporcionan como último recurso la función «window.onerror» que será llamada cuando se detecte un error de compilación o ejecución que impida continuar ejecutando el código javascript. Lo que podremos hacer para tratar el error será limitado pero al menos con log4javascript podremos enviar al servidor ese mensaje para que podamos primeramente conocer que se está produciendo y luego resolverlo, de otra manera nos sería desconocido ya que el código javascript se ejecuta en un entorno al que no tenemos acceso, en el navegador del usuario.

En el siguiente ejemplo mostraré como enviar al servidor esos errores de javascript que se produzcan. Para conseguirlo usaremos una combinación de la librería log4javascript comantada en la entrada anterior y la función window.onerror. A continuación el contenido de una página con el contenido completo usando RequireJS:

En la siguiente imagen se puede ver la petición que hace log4javascript para enviar el error al servidor.


Aparte de la propia traza del error que envía log4javascript (aunque en la imagen anterior no es muy descriptiva) podemos obtener otra información relacionada con la petición http que se realiza como el navegador en el que se ha producido, sistema operativo y versiones de ambos aparte de cookies si las hubiese o la dirección IP. En esa URL deberemos tener algo que atienda la petición y saque a los logs del servidor el mensaje enviado. En caso de usar Java probablemente hagamos uso de un servlet, u otra cosa en función del framework web que usemos, y utilizaremos slf4j, log4j o logback para sacarlo al registro de trazas del servidor. El código completo de este ejemplo está alojado en mi repositorio de GitHub.

Referencia:

Introducción y ejemplo de RequireJS
Introducción y ejemplo de Mustache
Logging en Javascript con log4javascript
Optimizar módulos de RequireJS y archivos Javascript