Una sinfonía en C#

Un pequeño aporte a la comunidad de habla hispana.

Backbonejs: La vista

Qué es la vista?

En ella vamos a mostrar nuestro modelo, en este caso al tratarse de un navegador web será en HTML, y responderá a la interacción del usuario.

Cómo creo una vista con Backbone?

Primero que nada utilizamos el mismo patrón que para crear un modelo, del siguiente modo:

var Vista = Backbone.View.extend({});

y luego para utilizarla

var vista = new Vista();

Definiciones y cosas que tenemos por defecto

Si no hacemos más nada que lo anterior nuestra vista no hace gran cosa, de hecho, no hace nada, lo que acabamos de lograr es extender la clase de Backbone para tener algunos métodos y propiedades (que vamos a usar después) pero en sí, por ahora, no hace mucho

El método render

Este método nos permitirá imprimir nuestra vista sobre algún otro elemento de la página, sin embargo backbone no nos da ninguna definición especial de este método (es decir, nada nos impide ponerle otro nombre) pero es lo más común hacerlo de este modo, vamos a ver una primera implementación

		var Vista = Backbone.View.extend({
			render: function(){
				return this;
			}
		});
				
		var vista = new Vista();
		
		$("#container").append(vista.render().el);	

Como vemos no hay nada especial, simplemente retornamos el mismo objeto vista en el método render (claro, es un ejemplo) la parte más importante del ejemplo es ver la línea final, luego de llamar al método render accedemos al objeto el, este objeto es parte de la clase base y va a contener el HTML que vamos a imprimir en la vista, entonces, siempre vamos a retornar this en nuestro método render para poder acceder a el y poner en la página. Si corremos este código vemos lo siguiente:

image

Por defecto el contenido de la vista es un simple div, el mismo se encuentra contenido en la propiedad el

La propiedad el de la vista

Como vimos en esta propiedad se guarda el contenido de nuestra vista, existe otra propiedad (muy similar) que se llama $el, que es igual que el pero tiene un wrapper de jQuery (o Zepto, o la biblioteca que hayamos usado para manipular el DOM), vamos a interactuar con esta propiedad cuando queramos manipular el contenido de nuestra vista.

El tagName

La propiedad tagName de la vista nos permite cambiar el tipo de elemento que genera, por defecto es un div pero si configuramos la propiedad como un strong por ejemplo es lo que obtendremos.

var Vista = Backbone.View.extend({
	tagName: "strong", //cambiamos el elemento de la vista
	render: function(){
		return this;
	}
});
		
var vista = new Vista();

$("#container").append(vista.render().el);

image

Usando un template en la vista

Como dije al principio backbone no nos dice mucho de la vista así que muchas de las cosas que hacen son como convenciones, una de ellas es asignar a la vista una propiedad template y colocar dentro código HTML que vamos a renderizar y con esto reemplazar totalmente el contenido del elemento el, por ejemplo:

var Vista = Backbone.View.extend({
	template: "<strong>hola</strong>",
	render: function(){
		this.el = this.template;
		return this;
	}
});
		
var vista = new Vista();

$("#container").append(vista.render().el);

image

Agregando el modelo a la vista

Bien, ahora vamos a intentar mostrar el contenido del modelo en la vista, para hacer vamos a crear un modelo como hicimos antes y pasarlo a la vista para que ésta lo muestre, el código sería así:

//definimos un modelo sin nada en especial
var Modelo = Backbone.Model.extend({
});	
//definimos una vista, vamos a usar el div por defecto
//en "el" para agregarle datos del modelo
var Vista = Backbone.View.extend({
	render: function(){
		//utilizamos $el para agregar el dato del modelo
		this.$el.append(this.model.get("texto"));
		return this;
	}
});
//creamos una instancia del modelo y asignamos la propiedad texto		
var modelo = new Modelo({texto: "hola mundo"});
//instanciamos una vista y pasamos el modelo en el constructor
//utilizamos la propiedad model
var vista = new Vista({model: modelo});
//renderizamos la vista
$("#container").append(vista.render().el);

image

Eventos, escuchando y cambiando el modelo

Una cosa interesante de backbone es que la comunicación entre componentes la podemos hacer totalmente a través de eventos, vamos a ver cómo hacemos para que la vista sepa que el modelo cambió.

Existe un método en la vista el cual es ejecutado al crearse la misma, que sirve las veces de constructor, aquí es donde nos “atachamos” a los eventos del modelo para saber que cambió, del siguiente modo:

//definimos un modelo sin nada en especial
var Modelo = Backbone.Model.extend({
});	
//definimos una vista, vamos a usar el div por defecto
//en "el" para agregarle datos del modelo
var Vista = Backbone.View.extend({
	initialize: function(){ //este método se ejecuta el crear una instancia
		this.model.on("change", this.render, this); 
		//escuchamos el evento "change" de nuestro modelo
		//y indicamos que cuando ocurra se ejecute el método
		//"render" (el último parámetro es el contexto)
	},
	render: function(){
		//utilizamos $el para agregar el dato del modelo
		this.$el.html(this.model.get("texto"));
		return this;
	}
});
//creamos una instancia del modelo y asignamos la propiedad texto		
var modelo = new Modelo({texto: "hola mundo"});
//instanciamos una vista y pasamos el modelo en el constructor
//utilizamos la propiedad model
var vista = new Vista({model: modelo});
//renderizamos la vista
$("#container").append(vista.render().el);

Si ahora ejecutamos la aplicación y desde la consola hacemos:

model.set("texto", "chau")

La vista se vuelve a dibujar.

Acá tenemos una lista completa de los eventos a los que nos podemos “atachar” entre ellos está, por supuesto”, el evento “invalid”.

Cambiar el contenido del modelo cuando algo cambia en la vista

Backbone tiene una forma de escuchar los eventos dentro de una vista con un sintaxis similar a la de jQuery, el único detalle a tener en cuenta es que el contexto sobre el que se trabaja es siempre el elemento $el, veamos un ejemplo

//definimos un modelo sin nada en especial
var Modelo = Backbone.Model.extend({
});	
//definimos una vista, vamos a usar el div por defecto
//en "el" para agregarle datos del modelo
var Vista = Backbone.View.extend({
	initialize: function(){ //este método se ejecuta el crear una instancia
		this.model.on("change", this.render, this); 
		//escuchamos el evento "change" de nuestro modelo
		//y indicamos que cuando ocurra se ejecute el método
		//"render" (el último parámetro es el contexto)
	},
	render: function(){
		//utilizamos $el para agregar el dato del modelo
		this.$el.html(this.model.get("texto"));
		return this;
	},
	events:{
		"click": function(){ //escuchamos el evento click
							 //sobre todo $el
			this.model.set("texto", "click!!"); //modificamos el modelo
		}
	}
});
//creamos una instancia del modelo y asignamos la propiedad texto		
var modelo = new Modelo({texto: "hola mundo"});
//instanciamos una vista y pasamos el modelo en el constructor
//utilizamos la propiedad model
var vista = new Vista({model: modelo});
//renderizamos la vista
$("#container").append(vista.render().el);

En este caso escuchamos el evento “click” sobre todo $el (porque no definimos agregamos un selector después del nombre del evento click) y cuando ocurre el evento modificamos el modelo, después, ya que estamos escuchando los cambios del modelo, automáticamente se vuelve a dibujar con el nuevo valor de la propiedad texto, para resumir dejo un link a un ejemplo un poco más completo, con un template.

Nos leemos.

Backbonejs: el modelo

Qué es el modelo?

El modelo es la abstracción que representa nuestro sistema, con sus datos y comportamiento, en principio debería ser totalmente independiente de los demás componentes de la aplicación (por ejemplo, debería ser independiente de la UI)

Creando un modelo con backbone

Para crear un modelo en backbone tenemos que hacer dos cosas:

  • Declarar una clase
  • Instanciarla

Para crear la clase de nuestro modelo necesitamos extender una clase de backbone, del siguiente modo:

var Task = Backbone.Model.extend();

con esto generamos una clase Task que extiende la clase Model de backbone, esto nos permite utilizar su funcionalidad, por ejemplo podemos hacer:

var task = new Task({name: "hacer algo"});
console.log(task.toJSON());

y obtenemos una representación JSON de nuestro modelo, esta funcionalidad la heredamos de la clase base de backbone.

Otra cosa que podemos hacer es tener propiedades por defecto, por ejemplo:

var Task = Backbone.Model.extend({
	defaults: {
		done: false
	}
});

de esta manera indicamos que la propiedad done, tiene un valor por defecto en false, entonces si hacemos

var task = new Task({name: "hacer algo"});
console.log(task.toJSON());

Obtenemos:

image

Leer y escribir propiedades

Si queremos leer o escribir alguna propiedad del modelo no podemos simplemente acceder a ella con el  (puntro)., es decir, no podemos hacer

	console.log(task.name);

Lo que necesitamos es acceder a los métodos get y set respectivamente

	task.set("name", "hablar con el dentista");
			
			
	console.log( task.get("name") );

No olvidemos que ahora estamos accediendo a un clase que hereda de otra, y además backbone necesita controlar el acceso a las propiedades del modelo para manejar las validaciones y notificaciones.

Validaciones

Para agregar validaciones a nuestro modelo tenemos que definir un método validate dentro de la declaración del mismo, por ejemplo

var Task = Backbone.Model.extend({
	defaults:{
		done: false
	},
	validate: function(attributes){
		if(attributes.name.length===0) return "the name is required";
	}
});

simplemente definimos el método validate, el cual recibe los atributos de la clase como parámetro y ponemos nuestro código de validación, por convención siempre que el método validate retorne algo distinto de void indicará un error de validación.

Para probarlo debemos hacer:

	
var Task = Backbone.Model.extend({				
		defaults:{
			done: false
			},				
		validate: function(attributes){					
			if(attributes.name.length===0) return "the name is required";				
		}			
	});
					
var task = new Task({name: "hacer algo"});			
task.set("name", "", {validate: true});			
console.log(task.toJSON());

Lo corremos y obtenemos:

image

La propiedad no cambió a causa del error de validación (el nombre deber existir), notemos que al hacer el set de la propiedad agregamos el parámetro {validate:true} esto es para que las validaciones se ejecuten al establecer el valor de la propiedad, podríamos obviarlo, dejar que el usuario establezca todos los valores y luego consultar el método isValid().

Eventos

Por último en esta escueta introducción al modelo vamos a hablar de eventos, podemos saber varias cosas relacionadas con el modelo

  • Cuándo cambió alguna propiedad
  • Cuándo cambia una propiedad particular
  • Cuándo hay un error de validación

Hay más pero por ahora está bien, un ejemplo completo de los visto hasta ahora más los eventos sería:

<html>
	<head>
		<script type="text/javascript" src="js/vendor/jquery/jquery.min.js"></script>
		<script type="text/javascript" src="js/vendor/underscore/underscore.js"></script>
		<script type="text/javascript" src="js/vendor/backbone/backbone.js"></script>			
	</head>
	<body>
		<div id="container" ></div>

		<script type="text/javascript">
			//creamos el modelo extendiendo la clase Model de backbone
			var Task = Backbone.Model.extend({
				//valores por defecto de algunas propiedades
				defaults:{ 
					done: false
				},
				//método de validación, si retorna algo indica que ocurrió un error
				validate: function(attributes){
					if(attributes.name.length===0) return "the name is required";
				}
			});
		
			//creamos un objeto a partir de la definición del modelo
			var task = new Task({name: "hacer algo"});
			
			//le asignamos un valor a la propiedad name
			task.set("name", "");
			
			//escuchamos el evento change del modelo
			task.on("change", function(){
				console.log("algo cambió");
				console.log("el valor anterior fue:");
				console.log(this.changed);
			});

			//agregamos un filtro al evento para indicar que nos interesa el name
			task.on("change:name", function(){
				console.log("el nombre cambió");
			});
			
			//si hay un error de validación cuando
			//hacemos set("propiead", "valor", {validate: true} se ejecuta este evento
			//también se ejecuta consultamos el método isValid() y existen errores
			task.on("invalid", function(){
				console.log("ocurrió un error de validación");
				console.log(this.validationError);
			});
		</script>
		
	</body>
</html>
		
	

Nos leemos, la próxima.

Introducción de Backbonejs

 

Backbone.js

introducción

Si nuestra aplicación web “single page” es compleja del lado del cliente o empieza a serlo, más tarde o más temprano vamos a necesitar aplicar las mismas prácticas de arquitectura que aplicamos del lado del servidor en Javascript (MVC, MVP, etc.), para esto hay varios patrones, bibliotecas y varios frameworks, hoy vamos a hablar de Backbonejs.

Qué es Backbone?

Es un framework una biblioteca para realizar aplicaciones en Javascript, utiliza el patrón MVP (esta parte es discutible ya que no está tan claro) y posee ciertos componentes definidos, está pensado para trabajar sobre una API RESTFul pero nada impide no hacerlo, sus componentes son:

  • Modelo
  • Vista
  • Colecciones
  • Rutas

Quién lo usa?

Es usado en sitios que seguramente has visitado, nombro algunos:

Y muchos más

Cómo lo agregamos a nuestro sitio?

Básicamente agregamos la referencia a nuestra aplicación (un único archivo de 6,5kB) tiene una dependencia la cual es underscore.js (del mismo autor Jeremy Ashkenas responsable también de Coffee Script) y por último también depende de alguna biblioteca para manipular el DOM que pueden ser jQuery , Ender, Zepto o cualquiera compatible, a partir de ahí podemos empezar a usarlo escribiendo Javascript como siempre, pero pudiendo utilizar los componente que nos provee o no; en resumen, la forma de incluirlo sería la siguiente:

<html>
<head>
	<script type="text/javascript" src="js/vendor/jquery/jquery.js"></script>
	<script type="text/javascript" src="js/vendor/underscore/underscore.js"></script>
	<script type="text/javascript" src="js/vendor/backbone/backbone.js"></script>
</head>
<body>
</body>
</html>

Cosas que nos da Backbone y cosas que no nos da:

Para terminar la introducción voy a listar los pro y contras que tiene Backbone (al menos para mí) desde el punto de vista de cada componente que lo forma.

Modelo:

  • + una base con funciones comunes para trabajar nuestro modelo.
  • + un método estándar de validaciones .
  • + funciones de serialización .
  • + podemos recuperar el estado anterior ante una modificación.
  • + notificaciones mediante eventos y la posibilidad de declararlos y dispararlos .
  • + algunas convenciones sobre ids y otras cosas.

Vista:

  • + las vista son más convenciones que otra cosa, Backbone nos da una base pero mayormente trabajamos siguiente un estándar de nombres y formas de hacer las cosas “de facto”.
  • + es muy siempre “atacharse” a los eventos de la vista.
  • = no tenemos binding automático (si bien es discutible si esto es bueno o malo) de todos modos se suele usar rivetsjs para esto.
  • = no tenemos un motor de templating dentro de Backbone (aunque ya que depende de underscore y éste tiene uno lo normal es usarlo) podemos usar el que queramos.

Ruteo

  • + nos permite utilizar navegación por hash ( # ) de manera muy sencilla y clara.
  • + el “binding” de parámetros es muy simple.

Colecciones

  • + Permiten agrupar listados de modelos (en principio del mismo tipo pero no es necesario en realidad)
  • + La manipulación de los datos es muy sencilla apoyado en funciones de underscore como where, find, etc.
  • + Son la interfaz para trabajar contra una API Restfull de manera transparente.

En general

  • + una gran ventaja de Backbone es que podemos usar cualquiera de sus componentes sin estar atados a los otros, por ejemplo podemos usar el motor de rutas solamente.
  • + también las definiciones son muy livianas y nos permite hacer cambios con facilidad y que la adopción no sea tan brusca.
  • + existe una gran cantidad de plugins para agregarle cosas que no tiene.
  • + soporta varias biliotecas de manipulación de DOM.
  • + soporta Requirejs.
  • + Es perfectamente apto para móviles de hecho Wallmart lo utiliza para su versión móvil.
  • - Creo que la documentación es el punto débil, siendo un framework usado en lugares tan importantes la documentación tiene muy pocos ejemplos y es bastante escueta (para mi gusto).
  • = Está muy enfocado en REST y la estructura de la aplicación y poco en la vista, esto puede ser o no bueno, depende del proyecto creo.

Debería usar Backbone?

Lo más importante tal vez sea esto, saber si necesitamos poner una dependencia tan importante en nuestro proyecto, yo creo que sí, y si no es Backbone será otra, es decir, mi respuesta es sí; y si preferimos un framework diferente a Backbone acá hay un lindo hilo sobre el tema.

Mi opinión personal es muy cercana a la de este video y este resumen, básicamente lo más valioso de Backbone es que no nos ata a una forma de hacer las cosas (claro, tampoco nos da mucha base de arquitectura pero la podemos encontrar en varios lugares), y si bien, no podemos pegar un tag y que funcione de forma mágica tenemos todo el control de lo que pasa y realmente si la cosa se llegase a complicar no es difícil encontrar problemas como en el caso de frameworks en los que hacen falta herramientas especiales para facilitar la depuración.

Bien, sin entrar mucho en detalle ni ser quisquilloso creo que esto es lo principal, no quiero aburrir con largas lectura, la próxima empezamos jugando con el modelo, pero para no quedarnos con las ganas dejo un link a un lindo ejemplo y el código del mismo, nos leemos.

Tips de Javascript: por qué se usa void(0)?

Muchas veces nos encontramos que en un elemento a (un link) se poner por defecto el valor del atributo href = javascript: void(0); lo cual es, al menos, llamativo, si no queremos hacer nada simplemente podríamos usar href= javascript:undefined, o no?

Undefined no siempre es lo que parece

Antes que nada vale aclarar que hay dos undefined en Javascript, el tipo, es decir, así como decimos que algo es un objeto, decimos que algo es undefined y también hay una variable global (una propiedad de window, es decir window.undefined)

Probemos este script

<html>
<head></head>
<body>
<script type="text/javascript">

undefined = 1;
var uno = 1;

console.log( uno == undefined);

</script>
</body>
</html>

Es claro que debería dar false (es decir no podemos decir que undefined vale 1, hablamos de la propiedad de window, entonces la comparación uno == undefined debería ser false). Sin embargo esto no es cierto para navegadores no tan viejos, por ejemplo IE8 (la versión más nueva que se puede usar en Windows XP, podemos comprobar esto usando el modo de emulación de IE10 y 11).

Si bien recientemente la especificación de ECMA script dice que la window.undefined es readonly esto no fue siempre así, entonces nada nos impedía (en navegadores no tan viejos, FF 6, Chrome 14, Safari 5, IE 8) hacer algo como

undefined = 1;

y listo, toda comparación contra undefined dejaba de funcionar correctamente, pero por qué alguien querría hacer eso? no lo sabemos, pero se puede.

Void siempre retorna undefined

Es así, el método void() siempre retorna el tipo undefined, la pregunta es por qué pasarle 0 como parámetro?, esto tal vez se deba a una cuestión semántica, es decir, queremos pasarle un valor para llamar la función y qué mejor que pasarle un cero si lo que realmente queremos es usarla para saber que algo no tiene un valor definido (esta parte la supongo, realmente no tengo idea)

Bueno, eso, nos leemos.

Bower: manejador de paquetes para Javascript

A medida que las aplicaciones crecen, crecen también en dependencias y muchas veces la tarea de comenzar una aplicación es tediosa y repetitiva al tener que agregar una y otra vez las mismas referencias o no.

Bower logo

Primero que nada, tener Nodejs instalado

Sí, Bower es un paquete de Nodejs, así que una vez que tenemos Nodejs instalado, ejecutamos el siguiente comando:

npm install -g bower

CERT_UNTRUSTED Error

Si esto ocurre lo más simple es ejecutar el siguiente comando

npm config set ca "" 

Si esto no funciona acá hay un par de opciones más

También es necesario git

Bower permite instalar paquetes desde cualquier repositrio git (incluso uno nuestro, remoto lo local) pero necesitamos tener git instalado y en el path para que Bower lo encuentre.

Ahora sí, bower instalado, empecemos

Una vez que tenemos Bower instalado podemos hacer lo mismo que hacemos con el comando npm de Nodejs, es decir, solicitar paquetes.

Entonces vamos a la consola y ejecutamos

bower install requirejs

image

si revisamos el directorio desde donde ejecutamos el comando vamos a ver que Bower creó una carpeta bower_components y puso el paquete ahí (lo que hay en el paquete depende de la definición de quien lo creó)

Cambiando el directorio de instalación

Esto es simple, creamos un archivo con el nombre .bowerrs (si, un archivo sin nombre con extensión bowerrc) y colocamos lo siguiente dentro

{	
"directory": "scripts"
}

Entonces ejecutamos de nuevo el comando y vemos que Bower pone todo dentro de la carpeta scripts

Automatizando tareas comunes

Una cosa interesante es tener un archivo con varias dependencias que siempre usamos y usarlo de base para instalar diferentes paquetes y versiones en nuestra aplicación, esto es simple. Creamos un archivo con el nombre bower.json y colocamos dentro lo siguiente

{	
"name": "app", "dependencies": {"backbone": null, "jquery": null, "requirejs": null}
}

Ahora con estos dos archivos en nuestro directorio no nos queda más que ejecutar bower install y listo (el null en cada componente indica la versión, en este caso es la última).

image

Un detalle interesante es que instaló underscorejs sin que lo pidamos, esto ocurre porque backbone tiene definido en su archivo de configuración de Bower que depende de underscore.

Esto es un buen ejemplo de un archivo de configuración

{
  "name"          : "backbone",
  "version"       : "1.1.2",
  "main"          : "backbone.js",
  "dependencies"  : {
    "underscore"  : ">=1.5.0"
  },
  "ignore"        : ["backbone-min.js", "docs", "examples", "test", "*.yml", "*.map", ".html", "*.ico"]
}

Como verán se pueden definir muchas cosas, acá podemos encontrar una referencia completa.

Entonces, a partir de ahora podemos tener nuestros archivos de configuración comunes para las aplicaciones y dejar que Bower haga todo por nosotros, o si tenemos un proyecto con dependencias de terceros simplemente agregamos en el código los archivos de configuración de Bower para que cada uno los instale.

Hasta la próxima.