lunes, 3 de febrero de 2014

Angular JS con Rails

Probando nuevos frameworks y arquitecturas para conseguir una mejor experiencia de usuario en aplicaciones web, ,he estado trabajando con Angular JS. Es un framework Javascript bastante potente, que permite tener aplicaciones en una sola página, aunque se puedan visualizar las 'rutas' dentro del navegador.

Mi primera idea fue usar rails junto con Angular JS, de modo que rails sirviera los ficheros estáticos, haciendo de backend para proveer de los datos necesarios a la aplicación web. Para esto, los controller de rails van a devolver todas (o casi todas) las peticiones en formato JSON.

Lo primero será incluir las librerías necesarias para angular. Esto se puede hacer mediante gemas, usando 'angularjs-rails', junto con underscore.

gem 'angularjs-rails'
gem 'underscore-rails'

Mediante estas gemas tendremos dentro del assets pipeline de la aplicación los ficheros necesarios para lanzar Angular con el frontend. En el fichero application.js habrá que incluir los ficheros de angular, tal y como indica la página de la gema.

También debemos preparar nuestro layout application.html.haml para que ejecute Angular JS. Para esto, dentro de la etiqueta HTML incluiremos la directiva de angular:

%html{'ng-app'=>"miaplicacion"}

Para empezar la aplicación, crearemos simplemente un controlador (home) con un index, de modo que sea el root_path, y será la única vista de la aplicación rails. En la vista incluiremos la directiva ng-view de angular JS:

.container{'ng-view'=>true}

De este modo, angular ejecutará la vista correspondiente, que deberemos definir en sus rutas. Esto lo indicaremos en el fichero principal de JS (o coffeeScript) de la aplicación: home.js.coffee en mi caso, que se incluirá desde application.js, eliminando el require self . Este fichero tendrá lo siguiente;

#= require_self
#= require_tree ./angular/controllers

@miaplicacion = angular.module('miaplicacion', ['ngRoute'])

@miaplicacion.config(["$httpProvider", (provider) ->
  provider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
])

@miaplicacion.config(['$routeProvider', ($routeProvider) ->
  $routeProvider.when('/jugador/new', { templateUrl: '../assets/jugadorCreate.html', controller: 'CreateJugadorCtrl' })
  $routeProvider.when('/jugadores', { templateUrl: '../assets/jugadores.html', controller: 'JugadorIndexCtrl' })
  $routeProvider.when('/jugador/:jugadorId', { templateUrl: '../assets/jugador.html', controller: 'JugadorCtrl' })

  # Default
  $routeProvider.otherwise({ templateUrl: '../assets/mainIndex.html', controller: 'HomeCtrl' })

])

Nuestra aplicación simplemente tendrá un modelo Jugador, con las operaciones básicas para listar, crear y mostrar. Incluimos el token para el CSRF, ya que deberá estar incluido en las llamadas a rails.
Toda la lógica de angular JS la tendremos en la carpeta angular dentro de nuestros javascripts.
Por otro lado, los templates de angular JS los hemos metido en una carpeta dentro de los assets de la aplicación.

Aunque no he incluido el código íntegro, llegué a finalizar los servicios básicos y las acciones sobre el modelo Jugador (guardando la entidad en una base de datos MongoDB), integrando la aplicación con Twitter Bootstrap. Tras este breve resumen, saco las conclusiones sobre estas pruebas con Angular JS:

En mi opinión, usar rails junto con Angular JS pierde efectividad por varios motivos. En primer lugar, rails es un framework MVC en sí. Sustituyendo la vista y gran parte de las funciones del controlador por Angular JS, supone perder gran parte de la potencia y utilidad del framework.
Además, se pierde totalmente el sentido de la arquitectura y la organización por carpetas, teniendo que meter todos los ficheros de angular dentro del asset pipeline de rails. Esto además supone una pérdida de tiempo al configurar la aplicación para su puesta en producción, y dificulta la separación de los assets estáticos a la lógica en sí de Angular.

Por tanto, para usar la potencia que nos da Angular JS, es conveniente usar como backend un framework que se adapte mejor, para aumentar la eficiencia y la facilidad de programación.