Un vistazo a Backbone

Backbone logo

Backbone es un framework MVC de javascript para clientes web que facilita el desarrollo de aplicaciones de una sola página. El hecho de aplicar MVC permite separar los datos de la presentación y Backbone lo hace sin invadir la página y con una serie de funciones que permiten la persistencia de datos en el servidor. Dicho de otro modo, podemos hacer páginas web que manejen registros de una BBDD sin estar recargando la página todo el rato, que es lo que ahora mola. También facilita las pruebas unitarias, permite aplicar distintos sistemas de plantillas, bla bla somos los mejores. Te recomiendo seguir la web oficial porque ha habido cambios (por ejemplo en el tema de validación, antes se hacía por defecto) y te puedes encontrar por ahí blogs desfasados (como este) que te confundirán. Si juegas con Backbone que no se te olvide incluir en la página el jquery, el underscore (backbone se apoya mucho en él) y por supuesto el backbone. Y en ese orden.

Y como todo framework MVC, Backbone nos obliga amablemente a separar el modelo, la vista y el controlador. Vamos a ver qué pinta tiene cada uno:

Model: El Modelo

Un model en Backbone viene a ser la clase que representa algo del dominio, es decir si la aplicación va de negocios pues en el modelo tendremos clases Cliente, Proveedor, etc.. la forma de definir un modelo de Backbone difiere algo de una clase convencional de Javascript con alguna peculiaridad extra: el método initialize, el hecho de que hay que usar set/get internamente (oh cielos), y el método validate con el que podemos hacer una validación de los atributos. Obviamente el modelo ignora al resto es una clase feliz que representa algo y que es ajena a las vistas y rutas.

/**
* This model represents a mighty warrior with classic attributes
* and methods like attack/defend
*/
 var Warrior = Backbone.Model.extend({
		defaults: {
			name: 'Frontend Warrior',
			strength: 6,
			agility: 6,
			intelligence: 6 
		},

		/*
		* default method invoked on init
		*/
		initialize: function(){
				console.log('Warrior Initialized:  ' + this.toString());
		},

		/*
		* default function to apply validation before setting any property
		*/
		validate: function(attribs,options){
			console.log('validation> Checking strength for: ' + attribs.name + ':' + attribs.strength);
			if(attribs.strength < 0 ) {
				console.log('validation> Invalid strength');
				return 'Invalid strength value';
			}
		},

		/**
		* attack method uses d6 roll and strength to return attack value
		*/
		attack: function() {
			return d6.roll() + this.get('stregnth');
		},

		/**
		* defend method uses d6 roll and agility to return attack value
		*/
		defend: function () {
			return d6.roll() + this.get('agility');
		},
		/**
		* dumps all warrior attributes
		*/
		toString: function () {
			return 'Name: ' + this.get('name') + 
				   ', Str: ' + this.get('strength') + 
				   ', Agl: ' + this.get('agility') + 
				   ', Int: ' + this.get('intelligence'); 
		}

});

var warrior1 = new Warrior({name: 'Brom'});

Collection: arrays de modelos

Dicho así queda como muy simplista pero básicamente es eso con algunos extras muy interesantes como una serie de utilidades para búsquedas, ordenar, etc... Crear una colección es bastante simple y ofrece varias alternativas para añadir, iniciar, extraer... Aquí tienes un ejemplo de modelo:

/**
* We define a Collection of warriors
* every element of the collection will have an id asigned
* (represented with cid attribute)
*/
var WarriorCollection = Backbone.Collection.extend({
	model: Warrior
});

var myWarriors = new WarriorCollection([warrior1,warrior2]);

myWarriors.add([warrior3]);

Las collections tienen una serie de métodos donde está la miga como sync, fetch, save que son los que mandan llamadas al servidor pero eso para otro día, para cerrar el círculo.

View: La Vista

Los views se utilizan para poder visualizar modelos. Se les pasa como parametro un model o una collection, se establece una plantilla que debe estar dentro del HTML y con un render se pintan los datos. En este ejemplo se usa la forma básica con el sistema de underscore, pero Backbone es neutral respecto al sistema de plantillas, pudiendo aplicar otros.

/**
* a view for the warrior.
* el is a reference to a dom element where we will display data.
*/
var WarriorView = Backbone.View.extend({
				template:_.template($('#warrior_template').html()),
				warrior: {},
				// called when instantiate
				initialize: function() {
				},

				// render
				render: function() {
					console.log(this.model);
					$(this.el).html(this.template(this.model));
				},
				renderAll: function() {
					console.log(this.collection);
					this.collection.each(this.renderOne, this);
				},
				renderOne: function (warrior) {
					$(this.el).append(this.template(warrior));
				}

	});
Router: Las Rutas

Esto sería lo que viene llamándose el controlador, aunque en los libros sobre Backbone se le da muchas vueltas a si esto eso MVC o MVP, en fin... básicamente es donde se hace el mapa de acciones. Backbone se queda con las peticiones que llegan a la propia página a través del... ¡¡¡hash!!! es lógico ya que es una forma muy simple de cambiar la url sin recargar la página, y ya se sabe, ahora lo que mola es NO cambiar de página (ay espera, que me ajuste las gafas de pasta y la camiseta de Naranjito que me pille en el FNAC). Es decir, si metemos un enlace en la página como: pagina.html#cliente/1 lo que recibe el router de backbone es #cliente/1 y con eso decide a qué función llama.

/**
* The router is the controller part of Backbone.
* Here we create the map of the actions supported by Warrior
* where url hashvalues are assigned javascript functions.
* e.g. thispage.html#author is mapped to js showAuthor function.
*/
var WarriorRoute = Backbone.Router.extend({
		routes : {
			'author' : 'showAuthor',
			'warrior/all' : 'showAllWarriors',
			'warrior/:id' : 'showWarrior',
			// default route
			'*other': 'default'

		},
		'showAuthor' : function () {
				console.log('author: Pello Xabier Altadill Izura');
			},
		'showAllWarriors' : function (id) {
				var warriorsToShow = myWarriors;
				console.log('Warriors to show: ' + warriorsToShow.size());
				var warrior_view = new WarriorView({ collection: warriorsToShow, el: $('#warrior')});
				warrior_view.renderAll();
			},
		'showWarrior' : function (id) {
				var warriorToShow = myWarriors.at(id);
				console.log('showing warrior id: ' + warriorToShow);
				var warrior_view = new WarriorView({ model: warriorToShow, el: $('#warrior')});
				warrior_view.render();
			},
		'default' : function () {
				console.log('sorry, route not supported');
		}
	});

var myRoutes = new WarriorRoute();

// We need this to make the routers work.
Backbone.history.start();

Bueno, otra pieza más en el puzzle, ya solo falta el pegamento con las otras.

Tienes este FEO (y escasamente funcional) ejemplo completo en está página, abre la consola de javascript para ver los logs.

by Pello Altadill 08/16/2013 01:37:50 - 297 hits

El ciclo de vida de una Activity en Android

Android tiene una forma peculiar de gestionar las aplicaciones que choca un poco con los sistemas operativos tradicionales. Pese a estar montado en un linux con el kernel 2.6, Android no tiene por costumbre matar las aplicaciones cuando las cerramos. La clave está en que igual lo hace o igual no, según las necesidades o lo que Android considere oportuno. La cosa es que no hay que empeñarse en tener un botón para matar la aplicación, oficialmente no se recomienda y se todo se resume a un tranqui...

by Pello Altadill 08/15/2013 13:55:06 - 517 hits

More »

Callbacks y funciones asíncronas, el estilo Node.js

El estilo nodejs Yo pensaba que sabía -algo- de javascript hasta que descubrí Node.js :P. En fin, espero ser siempre un aprendiz. Conforme te vas asomando a nuevas tecnologías tratas de hacer las cosas correctamente y como recién llegado aplicas lo de donde fueres haz lo que vieres. ¿Existe algún documento con las convenciones de Node.js? Sí, Node.js tiene una guía de estilo aunque no e...

by Pello Altadill 08/14/2013 22:08:41 - 559 hits

More »

Intents con parámetros y startActivityForResult

Cuando en Android queremos abrir una nueva pantalla o Activity se hace de una manera muy curiosa: se crea un Intent. Los Intent son eso, intentos. No garantizan que el resultado vaya a ser el correcto ni si quiera que alguien vaya a responder, y es que los intents se pueden usar tanto para abrir Activities como para solicitar cualquier cosa (iniciar una llamada, mandar un mensaje, etc... ). Con los intents en definitiva se llama a componentes de cualquier tipo. Pueden ser internos de la propia a...

by Pello Altadill 08/14/2013 12:57:24 - 379 hits

More »

Ejemplo de IndexedDB la BD de HTML5

HTML5 trae un montón de nuevas posibilidades al navegador. Se ha hablado mucho del formato de vídeo, del canvas, los websockets, de los nuevos controles de formulario pero a mí en particular me ha interesado especialmente aquello de una BD en el cliente. Es un tema que no está tan documentado como el resto y que en muchos libros de HTML5 o ni se menciona o se hace de pasada. Y lo que es peor, hay información en blogs pero a nada que esté desfasada los ejemplos no funcionan como la llamada a...

by Pello Altadill 08/13/2013 22:35:53 - 916 hits

More »

Eventos en nodejs

NodeJS está orientado a eventos por tanto disponer de mecanismos para definir nuestros propios eventos es algo fundamental. Como vamos a ver no tenemos más que importar el módulo events y a partir de ahí ya podremos trabajar con instancias de EventEmitter. Y como disponemos de herencia, podemos agregar la emisión de eventos a nuestras clases. Un ejemplo sencillo de eventos Este programa lee datos por consola y puede generar tres eventos Si el usuario escribe algo S...

by Pello Altadill 08/13/2013 10:37:21 - 216 hits

More »

Consejos para programar Android

Desarrollar aplicaciones para Android no es tan ágil como con otras tecnologías. Montar la aplicación configurando cada uno de los ficheros XML (cosa que está bien pensada y tal) es un poco pesado y lo peor es que cuando lo quieres probar para ver el resultado tienes que esperar mucho y encima lo más seguro es que casque y que la aplicación se pare de forma inesperada. Voy a dejar aquí algunos consejillos que espero resulten útiles. ...

by Pello Altadill 08/13/2013 09:40:38 - 509 hits

More »

Hibernate self join con XML

El hecho de echar un ojo a SGBD noSQL no significa que lo relacional esté proscrito, por eso hay que seguir investigando más escenarios de hibernate. Existen situaciones en las que una tabla o entidad se hace referencia a sí misma. El ejemplo típico es el de empleado con un campo idjefe que hace referencia a otro empleado. Otro mítico se da en los foros, donde un mensaje puede tener un id de mensaje padre. Siguiendo con nuestro ejemplo del ERP vamos a ver cómo apañar el mapeo a si mismo con la ta...

by Pello Altadill 08/12/2013 14:26:02 - 309 hits

More »

Almacenamiento local en HTML5

Local Storage Los navegadores modernos van disfrutando de nuevas capacidades impulsadas por HTML5. Una de las más llamativas es la posibilidad del almacenamiento local, en el cliente, que viene en dos formas: por un lado en dos arrays relacionales que nos permiten guardar datos con un límite supuesto de 5Mb (al estilo del $_SESSION de php pero en el cliente) y por otra parte la posibilidad de tener una base de datos en el cliente. Está última, que un principio parecía iba a ser SQLite fina...

by Pello Altadill 08/11/2013 22:51:12 - 260 hits

More »