SlideShare una empresa de Scribd logo
1 de 47
Descargar para leer sin conexión
Primeros pasos con Backbone.js
Introducción a Backbone.js por Xavi Aznar
Índice
1. Primeros pasos 4
1.1. Requisitos básicos . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2. Enlazando Backbone . . . . . . . . . . . . . . . . . . . . . . 4
2. Modelos 5
2.1. Nuestro primer modelo . . . . . . . . . . . . . . . . . . . . . 5
3. Colecciones 9
4. Vistas 19
5. Plantillas 25
6. Controladores y Eventos 30
7. Eventos y vistas 31
8. Single Page Applications (aplicaciones de una sola página) 33
9. Routers 37
10.Comunicación con el servidor 40
2
ÍNDICE ÍNDICE
Comentarios de la adaptación del tutorial al
formato offline
Este eBook es la versión offline del tutorial que se encuentra colgado
en http://bit.ly/1erosPasosBackbone. El tutorial fue pensado como un
bloc de notas donde recoger mis progresos con Backbone. Poco a poco
se fue convirtiendo en un manual con el que iniciarse con uno de los
frameworks más potentes del momento en Javascript.
El texto está creado para ser leído en el navegador, donde los fragmen-
tos de código pueden ejecutarse directamente en la consola de Javas-
cript.
Al realizar la adaptación a un formato offline, como el PDF, esta posi-
bilidad desaparece. Para evitar que el lector se quede sin el feedback
proporcionado por la consola de Javascript, cada fragmento de código
ejecutable en la web se acompaña de una captura de pantalla de la sa-
lida por consola. De esta manera, se consigue una experiencia similar
a la de leer el tutorial en la web. Cuando el código manipula el DOM
-por ejemplo, cuando se trata de vistas-, el texto incluye capturas de la
página web antes y después de la ejecución del código.
En cuanto a los enlaces que aparecen en la página web, en la versión
offline se han complementado con una nota al pie. El enlace sigue sien-
do válido -clickable- en un formato como el PDF, pero se incluye la url
como una nota a pie de página por si quieres imprimir el tutorial como
referencia.
Otro elemento que se queda fuera de la versión offline son los comen-
tarios. A través de los comentarios puedes expresar dudas, quejas,
agradecimientos, sugerencias, puntualizaciones, correcciones... Para
mí son extremadamente importantes, pues me dicen qué hago bien,
qué puedo mejorar... Me hacen saber que hay alguien ahí, al otro lado
a quienes sirve este tutorial... Por tanto, te animo a visitar la página
http://bit.ly/1erosPasosBackbone y hacer oir tu voz.
Esta adaptación la he realizado utilizando LYX (http://www.lyx.org/),
un programa libre y multiplataforma, que permite utilizar la potencia
y flexibilidad del TEX y LATEX a través de un entorno gráfico que facilita
su utilización.
3
1 PRIMEROS PASOS
1. Primeros pasos
1.1. Requisitos básicos
En primer lugar, necesitas un navegador compatible con herramientas
de desarrollador para ver qué está pasando en la consola. Cualquier
navegador moderno servirá, aunque para elaborar este tutorial he uti-
lizado Google Chrome.
Para abrir la consola en Google Chrome, haz click en cualquier lugar
de la página web y elige Inspect Element del menú contextual. Selec-
ciona Console en la parte superior del panel con las herramientas de
desarrollo. También puedes pulsar Crtl + Shift + J (o a través del
menú de Chrome → Tools → Javascript console).
1.2. Enlazando Backbone
Para poder praticar con Backbone, es recomendable que crees una pá-
gina web en la que enlaces los diferentes componentes que requiere
Backbone: jQuery, Underscore, json2 y backbone:
<script type="text/javascript" src="jquery/jquery.min.js"></script>
<script type="text/javascript" src="backbone/json2.min.js"></script>
<script type="text/javascript"
src="backbone/underscore.min.js"></script>
<script type="text/javascript" src="backbone/backbone.min.js"></script>
Aunque Backbone.js sólo tiene como dependencia indispensable a underscore,
para realizar manipulación del DOM y poder gestionar persistencia
RESTful completa, necesitas también json2 y jQuery.
En realidad, json2 sólo es necesario si quieres dar soporte a navegado-
res antiguos que no tengan soporte nativo para JSON, lo que cada día
es menos frecuente.
Para seguir los ejercicios de este tutorial, mantén siempre
abierta la consola de Javascript de tu navegador.
4
2 MODELOS
2. Modelos
2.1. Nuestro primer modelo
En primer lugar, creamos un modelo extendiendo la clase Model de
Backbone. De esta forma creamos nuestra clase personalizada de Mo-
delo. No parece un gran avance, pero en realidad Backbone nos ha pro-
porcionado una serie de métodos para establecer (set) y obtener (get)
propiedades de nuestro modelo, entre muchas otros métodos útiles.
Utilizamos la convención habitual de que los nombres de las
clases empiezan por mayúscula y las instancias, por minús-
culas.
El siguiente código muestra en la consola la creación de un modelo:
Contacto = Backbone.Model.extend();
contacto = new Contacto();
console.log( contacto );
En la consola vemos que se ha creado algo... Utilizamos el método
.toJSON() para convertir el objeto a formato JSON.
5
2.1 Nuestro primer modelo 2 MODELOS
Utilizamos los métodos heredados de Model para establecer y obtener
la propiedad nombre:
Contacto = Backbone.Model.extend(); contacto = new Contacto();
contacto.set( ’nombre’, ’Anthony Machine’ );
console.log( contacto.toJSON() ); console.log( ’El nombre del contacto
es ’ + contacto.get( ’nombre’ ) );
Durante la creación de nuestra instancia del modelo podemos pasar
pares de clave:valor en un solo paso:
Contacto = Backbone.Model.extend();
contacto = new Contacto({
’nombre’: ’Anthony Machine’,
’telefono’: ’555-123-456’
});
console.log( contacto.toJSON() );
console.log( ’El telefono de ’ + contacto.get( ’nombre’ ) + ’ es el ’
+ contacto.get( ’telefono’ ) );
También podemos especificar valores por defecto en la creación de
nuestro modelo personalizado. Los valores por defecto se asignan si no
se especifica un valor para la propiedad (como en el caso del telefono
del segundo contacto):
Contacto = Backbone.Model.extend({
defaults: {
’nombre’: ’John Doe’,
6
2.1 Nuestro primer modelo 2 MODELOS
’telefono’: ’900-123-456’
}
});
contactoDesconocido = new Contacto();
contactoManoli = new Contacto({ ’nombre’: ’Manoli’ });
console.log( contactoDesconocido.toJSON() );
console.log( contactoManoli.toJSON() );
También podemos asignar funciones que actualicen una propiedad de
forma programable. En este caso, lanzamos la función desde la consola
para actualizar el nombre de un contacto creado con los valores por
defecto.
Contacto = Backbone.Model.extend({
defaults: {
’nombre’: ’John Doe’,
’telefono’: ’900-123-456’
},
actualizaNombre: function(){
var nuevoNombre = prompt( ’Introduce el nuevo nombre para el
contacto: ’ );
this.set( {’nombre’: nuevoNombre} );
}
});
contacto = new Contacto();
console.log( contacto.toJSON() );
contacto.actualizaNombre();
console.log( contacto.toJSON() );
En primer lugar, preguntamos al usuario el nombre que quiere asignar
al contacto y después mostramos en la consola cómo hemos actualiza-
do la propiedad nombre del contacto con el valor proporcionado.
7
2.1 Nuestro primer modelo 2 MODELOS
Finalmente, vamos a añadir una función que vigile los cambios que
se producen en la instancia del modelo creado y que nos avise al pro-
ducirse un cambio. Este tipo de funciones se denominan listeners en
inglés.
El listener escucha cambios en el valor de nombre y lanza una función
que registra el cambio en la consola.
Contacto = Backbone.Model.extend({
defaults: {
’nombre’: ’John Doe’
},
actualizaNombre: function( nuevoNombre ){
this.set({ ’nombre’: nuevoNombre });
}
});
// Creación del contacto con el nombre por defecto
contacto = new Contacto();
console.log( contacto.toJSON() );
// Listener
8
3 COLECCIONES
contacto.on( ’change:nombre’, function(){
console.log( ’Nombre del contacto modificado!’ )
} );
// Actualizamos el nombre
contacto.actualizaNombre( ’Miguel Campoviejo’ );
console.log( contacto.toJSON() );
3. Colecciones
Las colecciones (Collection) son conjuntos ordenados de modelos.
Como en el caso de los modelos, para crear una colección extendemos
la clase Collection proporcionada por Backbone.
Como una colección no es más que un conjunto de modelos, al crear la
colección debemos especificar el tipo de modelo que contiene. También
tenemos que especificar una url. Esto es necesario porque al realizar al-
guna operación CRUD, Backbone intenta conectar mediante REST con
la capa de persistencia, es decir, un servidor al otro lador de la URL que
le proporcionamos; allí completará la acción de creación, actualización
o eliminación de los datos de la colección. Si no proporcionamos esta
URL, aparecerán errores en la consola de Javascript.
Creamos la colección y la instanciamos, mostrando el resultado en la
consola usando el método .toJSON():
Contacto = Backbone.Model.extend();
Contactos = Backbone.Collection.extend({
Model: Contacto,
9
3 COLECCIONES
url: "#"
});
contactos = new Contactos();
console.log( contactos.toJSON() );
El resultado es un array vacío. Una colección es un array de objetos,
pero la colección no contiene todavía ningún elemento. Utilizamos el
método .add() para añadir modelos a la colección.
Contacto = Backbone.Model.extend();
Contactos = Backbone.Collection.extend({
Model: Contacto,
url: "#"
});
contactos = new Contactos();
contactos.add({ ’nombre’: ’Anthony Machine’ });
console.log( contactos.toJSON() );
10
3 COLECCIONES
Podemos añadir tantos objetos como queramos, repitiendo la acción
.add():
Contacto = Backbone.Model.extend();
Contactos = Backbone.Collection.extend({
Model: Contacto,
url: "#"
});
contactos = new Contactos();
contactos.add({ ’nombre’: ’Anthony Machine’ });
contactos.add({ ’nombre’: ’Miguel Campoviejo’ });
console.log( contactos.toJSON() );
En vez de añadir los elementos de la colección uno a uno, podemos
añadirlos durante la creación de la instancia de la colección:
Contacto = Backbone.Model.extend();
Contactos = Backbone.Collection.extend({
model: Contacto,
url: "#"
});
contactos = new Contactos([
{ ’nombre’: ’Anthony Machine’ },
11
3 COLECCIONES
{ ’nombre’: ’Miguel Campoviejo’ }
]);
console.log( contactos.toJSON() );
Podemos añadir un contacto a la colección en la posición que deseemos
utilizando at. El siguiente código añade contacto1 en la posición 1
usando at, mientras que el contacto2 se añade al final (posición por
defecto).
Del mismo modo, usamos remove para eliminar un elemento de la co-
lección:
Contacto = Backbone.Model.extend();
contacto1 = new Contacto({ ’nombre’ : ’Mary Sun’ });
contacto2 = new Contacto({ ’nombre’ : ’Federico Mercurio’ });
Contactos = Backbone.Collection.extend({
model: Contacto,
url: "#"
});
contactos = new Contactos([
{ ’nombre’: ’Anthony Machine’ },
{ ’nombre’: ’Miguel Campoviejo’ }
]);
contactos.add( contacto1, {at : 1} );
console.log( contactos.toJSON() );
12
3 COLECCIONES
contactos.add( contacto2 );
console.log( contactos.toJSON() );
// Eliminamos el primer elemento :
primerElemento = contactos.at(0);
contactos.remove( primerElemento );
console.log( contactos.toJSON() );
// Eliminamos un objeto determinado
contactos.remove( contacto2 );
console.log( contactos.toJSON() );
Vamos a verlo con un poco más de detalle;
1. Creamos la colección con dos contactos, y añadimos contacto1
en la posición 1. Después, añadimos contacto2 sin especificar la
posición, de manera que se añade al final de la colección:
13
3 COLECCIONES
2. Eliminamos el contacto situado en la posición 0 de la colección:
14
3 COLECCIONES
3. Finalmente, eliminamos el contacto2 (no hace falta especificar la
posición; Backbone lo busca y lo elimina esté donde esté):
15
3 COLECCIONES
Si queremos actuar sobre todos los elementos de la colección, un mé-
todo como .each() nos resultará útil. En el siguiente ejemplo, recorre-
mos los elementos de la colección y mostramos el nombre en la consola:
Contacto = Backbone.Model.extend();
Contactos = Backbone.Collection.extend({
model : Contacto,
url : ’#’
});
contactos = new Contactos([
{ ’nombre’ : ’Anthony Machine’ },
{ ’nombre’ : ’Miguel Campoviejo’ },
{ ’nombre’ : ’Mary Sun’ }
]);
contactos.each( function( cadaContacto ){
console.log( ’El nombre del contacto es ’ + cadaContacto.get(
’nombre’ ) );
16
3 COLECCIONES
} );
Finalmente, podemos establecer listeners que controlen si se producen
cambios en la colección. En el siguiente ejemplo, vigilamos el evento
add.
Contacto = Backbone.Model.extend();
Contactos = Backbone.Collection.extend({
model: Contacto,
url: ’#’
});
contactos = new Contactos();
//Listener
contactos.on( ’add’, function(){
console.log( "Has cambiado la colección!" );
} );
var nuevoContacto = new Contacto({
’nombre’ : ’Anthony Machine’
});
contactos.add( nuevoContacto );
17
3 COLECCIONES
Un solo listener puede reaccionar a diferentes eventos; para ello sólo
tenemos que incluir los eventos separados por espacios. Modificamos
el código anterior para reaccionar tanto a la adición como a la elimi-
nación de elementos de la colección.
Contacto = Backbone.Model.extend();
Contactos = Backbone.Collection.extend({
model: Contacto,
url: ’#’
});
contactos = new Contactos();
//Listener
contactos.on( ’add remove’, function(){
console.log( "Has cambiado la colección!" );
} );
var nuevoContacto = new Contacto({
’nombre’ : ’Anthony Machine’
});
console.log( ’(Contacto añadido)’ )
contactos.add( nuevoContacto );
console.log( ’(Contacto eliminado)’ );
contactos.remove( nuevoContacto );
18
4 VISTAS
4. Vistas
El proceso para crear una vista (view en inglés) es extendiendo la clase
View proporcionada por Backbone. Creamos la clase sin ningún pa-
rámetro y examinamos qué es lo que nos proporciona una vista en
Backbone.
VistaPrincipal = Backbone.View.extend();
vistaPrincipal = new VistaPrincipal();
console.log( vistaPrincipal );
Al crear una vista obtenemos un objeto con diferentes propiedades. La
propiedad cid es el identificador interno asignado por Backbone a la
vista recién creada. Sin embargo, nos interesan más las propiedades
el y $el (abreviatura de elemento).
La propiedad el es el contenedor de la vista creada. Por defecto, Back-
bone proporciona un div. Puedes pensar en una vista como una caja
en la que mostrar el interfaz de la aplicación web. Todo lo que ten-
gas que dibujar (render en inglés) en el interfaz -botones, formularios,
listas, etc- se mostrará dentro del elemento el de la vista.
Sin embargo, no debes pensar que la vista contiene el HTML de tu
aplicación; contienen la lógica necesaria para presentar al usuario los
datos contenidos en los modelos. La representación de los datos se
realiza, en general, usando algún sistema de plantillas como los mi-
crotemplates de Underscore.js, Mustache, etc... Después de recoger los
19
4 VISTAS
datos y combinarlos con la plantilla, la vista almacena el resultado en
el elemento el.
El siguiente ejemplo muestra como, por defecto, el elemento el de una
vista es un div vacío:
VistaPrincipal = Backbone.View.extend();
vistaPrincipal = new VistaPrincipal();
console.log( vistaPrincipal.el );
Por otro lado, $el es la referencia vía jQuery al objeto contenedor de la
vista (el div, en este caso). Así podemos hacer referencia al elemento y
utilizar los métodos proporcionados por jQuery para manipularlo. Por
ejemplo, $el.show(); sería equivalente a $(’div’).show(); (aunque
probablemente deberías ser más preciso con el selector).
Backbone proporciona un método render para dibujar la vista, aunque
por defecto no hace nada. Backbone permite utilizar cualquier sistema
para mostrar la interfaz en pantalla, así que debemos especificar cómo
se muestra: utilizando concatenación de cadenas, mediante un motor
de plantillas...
Empezamos utilizamos el método .append() de jQuery para añadir un
encabezado en la vista:
VistaPrincipal = Backbone.View.extend({
render: function(){
this.$el.append( ’<h1>Hola Backbone!</h1>’ );
return this;
}
});
20
4 VISTAS
vistaApp = new VistaPrincipal();
vistaApp.render();
console.log( vistaApp.el );
Al llamar a la función render() de forma manual, hemos forzado a
dibujar el encabezado en el div de la vista. Sin embargo, como he-
mos utilizado .append(), si lanzamos .render() múltiples veces, el
contenido se añade también múltiples veces...
VistaPrincipal = Backbone.View.extend({
render: function(){
this.$el.append( ’<h1>Hola Backbone!</h1>’ );
return this;
}
});
vistaApp = new VistaPrincipal();
vistaApp.render();
vistaApp.render();
vistaApp.render();
console.log( vistaApp.el );
21
4 VISTAS
Si esto no es lo que queremos, podemos utilizar .html() para estable-
cer el contenido de la vista, por ejemplo.
VistaPrincipal = Backbone.View.extend({
render: function(){
this.$el.html( ’<h1>Hola Backbone!</h1>’ );
return this;
}
});
vistaApp = new VistaPrincipal();
vistaApp.render();
vistaApp.render();
vistaApp.render();
console.log( vistaApp.el );
Si queremos que la vista se dibuje automáticamente, podemos utilizar
el método initialize. initialize se ejecuta al crear la instancia de
la vista.
VistaPrincipal = Backbone.View.extend({
22
4 VISTAS
initialize: function(){
this.render();
},
render: function(){
this.$el.html( ’<h1>Hola Backbone!</h1>’ );
return this;
}
});
vistaApp = new VistaPrincipal();
console.log( vistaApp.el );
Hasta ahora, toda la acción ha tenido lugar en la consola de Javascript.
El siguiente paso será conseguir mostrar la aplicación web en el nave-
gador. Backbone asigna por defecto al elemento el un div si no se ha
especificado tagName, className o id.
En el siguiente código asignamos una etiqueta H1 mediante tagName al
elemento el. Durante la inicialización de la vista, podemos establecer
variables, etc. En nuestro caso, preguntamos al usuario por su nom-
bre. Después, llamamos a la función render, que se encarga de dibu-
jar la vista. Finalmente, utilizamos jQuery para añadir la vista al DOM,
dentro de un div con id=vista01. Este elemento div id="vista01"
podría ser cualquier elemento previo del DOM, como body, por ejemplo.
Una vez la vista se añade al DOM, aparece en pantalla.
VistaPrincipal = Backbone.View.extend({
tagName : ’h1’,
initialize: function(){
var nombre = prompt ( ’Como te llamas? n (Por defecto =
Backbone)’ );
23
4 VISTAS
nombre = nombre || ’Backbone’;
this.render( nombre );
},
render: function( nombre ){
this.$el.text( ’Hola ’ + nombre +’!’ );
$( ’#vista01’ ).html( this.el );
return this;
}
});
vistaApp = new VistaPrincipal();
Entre el fragmento de código y el siguiente párrafo tenemos div id="vista01"
(vacío):
Cuando ejecutamos el código, se pregunta al usuario:
Se genera la vista y se añade al DOM, justo después del fragmento de
código:
24
5 PLANTILLAS
En Backbone, las vistas incorporan también la funcionalidad asociada
a los elementos de la interfaz gráfica de la aplicación web. Es decir, que
además de la "V" (de Vista), del modelo MVC, también contienen los
controladores (la "C", del modelo MVC).
Al pulsar un botón, añadir un elemento o modificar algún dato en la
aplicación, establecemos listeners para los eventos que se disparen, y
así hacer que la aplicación actualice aquello que sea necesario.
5. Plantillas
Antes de entrar en el tema de la funcionalidad encapsulada en las vis-
tas, creo que es conveniente comentar cómo mostrar en las vistas los
datos contenidos en los modelos.
Backbone nos permite mostrar los datos contenidos en los modelos co-
mo queramos; no nos obliga a hacerlo de una forma determinada. El
método más básico y directo, es simplemente obteniendo los datos des-
de el modelo mediante un modelo.get( ’nombre’ ) e insertándolo en
el DOM.
Aunque podemos concaternar los valores obtenidos de los modelos con
etiquetas HTML y añadirlas directamente al DOM del documento, esta
solución no es la más recomendable.
Underscore.js proporciona un sistema de microplantillas que podemos
utilizar por defecto en nuestras aplicaciones, ya que underscore.js es
una dependencia de Backbone.js.
La función template permite insertar la información contenida en un
modelo, convirtiéndola a formato JSON, en un documento HTML. Para
ello, especificamos el nombre de la "clave" entre < %= ... %>, como se
muestra en el siguiente ejemplo:
25
5 PLANTILLAS
plantilla = _.template( ’Hola < %= nombre %>. Me encanta tu tema < %=
cancion %>!’ );
console.log(
plantilla( {nombre: ’Anthony Machine’, cancion: ’Little black
angels’} )
);
_.template(...) analiza el código que le pasamos en busca de las
etiquetas < %= ... %> que indican dónde debe insertar los valores del
modelo. Si coinciden el nombre de la etiqueta y la clave del modelo,
inserta el valor correspondiente. Si no lo encuentra, se muestra un
error en la consola. Finalmente, devuelve el código de la plantilla con
los valores del modelo ya insertados.
Sin embargo, colocar trozos de código HTML junto con las etiquetas
del motor de plantillas proporcionado por Underscore.js en el código
javascript de la aplicación enturbia el código, haciéndolo menos legible
y claro.
La solución pasa por utilizar la otra dependencia de Backbone, jQuery
y su capacidad para seleccionar y extraer elementos del DOM del do-
cumento HTML.
En el siguiente ejemplo selecciono el contenido del menú de navegación
con el contenido de este documento y lo muestro a través de la consola:
var $menu = $(’#menu ul’).html()
console.log( $menu );
26
5 PLANTILLAS
Como ves, sólo necesitas saber qué quieres seleccionar. Puedes utilizar
el id, el nombre de la class o la etiqueta (entre otras muchas opcio-
nes).
La idea será colocar el HTML de la plantilla que quieres utilizar para
tus datos de la aplicación en el propio documento HTML. Para evitar
que aparezca en el navegador, lo encerramos entre etiquetas script.
Aunque el navegador no lo muestre, intentará interpretarlo como si
fuera javascript (por defecto). Pero tampoco es éso lo que queremos, así
que especificamos type="text/template; el navegador no sabrá como
interpretarlo y por tanto, lo ignorará. En realidad podemos clasificarlo
como text/LoQueSea, pero por convención se utiliza el tipo template
(plantilla, en inglés).
Finalmente, para poder acceder al fragmento de código que contiene la
plantilla con facilidad, lo designamos con un id único.
El fragmento de código a continuación muestra cómo podría ser una
plantilla para mostrar la información del siguiente ejemplo:
<script type="text/template" id="plantilla-vistapersonaje">
<h1> < %= nombre %> </h1>
<p>
<a href="< %= wikipediaurl %>" class="mas-info">
Información en la wikipedia sobre < %= nombre %>
</a>
</p>
</script>
27
5 PLANTILLAS
Ahora, el código de la vista para el contacto será:
Personaje = Backbone.Model.extend();
VistaPersonaje = Backbone.View.extend({
el: ’#vista02’,
plantilla: _.template( $(’#plantilla-vistapersonaje’).html() ),
initialize: function( modelo ){
this.$el.html( this.plantilla ( modelo.toJSON() ));
},
render: function(){
$(’#vista02’).html( this.$el );
return this;
}
});
personaje = new Personaje({
’nombre’: ’Antonio Machín’,
’url’: ’http://es . wikipedia . org/wiki/Antonio_Mach %C3%ADn’
})
vistaPersonaje = new VistaPersonaje( personaje );
Antes de ejecutar el código:
Y después, cuando hemos generado la vista a partir de la información
contenida en el modelo:
De esta forma conseguimos separar el código HTML de la plantilla de
la funcionalidad relacionada con la vista. No hace falta preocuparse
28
5 PLANTILLAS
sobre cómo se van a mostrar los datos de la aplicación, de los estilos
o etiquetas en los que se encuentran. Sólo tienes que saber que en la
plantilla habrá un hueco con la clave del dato que se insertará en esa
posición (y dejar el diseño a un especialista, por ejemplo).
Si se modifica la plantilla...
<script type="text/template" id="plantilla-vistapersonaje2">
<ul>
<li> <a href="< %= url %>"> < %= nombre %> </a></li>
<li> <a href="< %= url %>"> < %= nombre %> </a></li>
<li> <a href="< %= url %>"> < %= nombre %> </a></li>
<li> <a href="< %= url %>"> < %= nombre %> </a></li>
</ul>
</script>
...el código no cambia:
Personaje = Backbone.Model.extend();
VistaPersonaje = Backbone.View.extend({
el: ’#vista02’,
plantilla: _.template( $(’#plantilla-vistapersonaje2’).html() ),
initialize: function( modelo ){
this.$el.html( this.plantilla ( modelo.toJSON() ));
},
render: function(){
$(’#vista02’).html( this.$el );
return this;
}
});
personaje = new Personaje({
’nombre’: ’Antonio Machín’,
’url’: ’http://es . wikipedia . org/wiki/Antonio_Mach %C3%ADn’
})
vistaPersonaje = new VistaPersonaje( personaje );
sin embargo, la salida sí que es diferente, pues la plantilla que hemos
utilizado es diferente:
29
6 CONTROLADORES Y EVENTOS
Hasta ahora, sabemos que la vista tiene una propiedad el, que contie-
ne una referencia a un elemento del DOM del documento. Mediante la
función _.template(...) de Underscore.js, extraemos el HTML de la
plantilla y lo combinamos con los datos almacenados en el modelo. Y
mediante render(...) lo dibujamos en el documento HTML.
Sin embargo, antes de empezar este apartado sobre plantillas decía que
las vistas también contienen funcionalidad asociada a los elementos
visuales de la aplicación. Vamos con esta funcionalidad...
6. Controladores y Eventos
En el modelo MVC una aplicación se encuentra separada en tres tipos
de componentes. En Backbone.js los controladores se incluyen en las
vistas, con lo que no podríamos hablar estrictamente de un modelo
MVC .
En realidad, en las primeras versiones de Backbone.js sí que
había una clase controller, solo que en la versión 0.5.0 se
renombró a router. Más adelante hablo de los routers.
El controlador no es más que una función que se encarga de reaccionar
a las acciones que realiza el usuario sobre los componentes visuales de
la aplicación y actualiza los datos del modelo en consecuencia.
Para poder reaccionar a las acciones del usuario necesitamos un siste-
ma que gestione los eventos que éste produce (hacer click en un botón,
enviar un formulario, etc).
30
7 EVENTOS Y VISTAS
Backbone.js proporciona un sistema de gestión de eventos que permite
asociar listeners a los selectores incluidos en el elemento el (o directa-
mente a el si no se proporciona ningún selector). Los eventos se recogen
en forma de hash de la forma "evento selector" : "callback".
El siguiente ejemplo muestra lo que podría ser un hash de eventos para
una aplicación de agenda de contactos:
events : {
’dblclick label’ : ’muestraEditaContacto’,
’blur .editar’ : ’cierraEditaContacto’,
’click .elimina’ : ’eliminaContacto’
}
muestraEditaContacto, cierraEditaContacto y eliminaContacto
son funciones que deben estar definidas en el mismo ámbito de la vista.
En realidad, Backbone proporciona un sistema de eventos muy po-
tente, que va más allá de las vistas y que permite gestionar eventos
de modelos, colecciones, routers, el historial y de los objetos y eventos
propios que definas para tu aplicación.
Antes de centrarme en el uso de eventos relacionados con las vistas,
comentaré brevemente los Eventos en Backbone.js.
Backbone.Events puede utilizarse para asociar eventos personaliza-
dos a cualquier objeto que definamos.
7. Eventos y vistas
Una de las ventajas de Backbone.js es que los diferentes componentes
que integran la aplicación -modelos, vistas y el DOM- se comunican
31
7 EVENTOS Y VISTAS
entre sí. Esta comunicación es bidireccional, tanto si se actualiza la
información contenida en el modelo como si se genera un evento en la
vista que afecta al modelo.
En Backbone.js la Vista hace de concentrador de todo lo que sucede
en nuestra aplicación. Ya hemos visto antes que un cambio en el mo-
delo permitía, a través de un listener, reaccionar ante un cambio en
el modelo. En general, cuando cambia la información contenida en los
modelos de nuestra aplicación querremos actualizar la vista, mostran-
do la información actualizada.
Del mismo modo, cuando se produce un evento en la vista, actualiza-
mos el modelo o la colección correspondiente.
32
8 SINGLE PAGE APPLICATIONS (APLICACIONES DE UNA SOLA
PÁGINA)
8. Single Page Applications (aplicaciones de
una sola página)
Hasta ahora hemos estado practicando directamente en la línea de co-
mando, creando modelos, colecciones y vistas. También hemos visto
que una de las ventajas de Backbone es que todos los elementos se
comunican entre sí, a través de eventos asociados a los diferentes mo-
delos o vistas de nuestra aplicación web. De hecho, Backbone.js pro-
porciona toda la infraestructura necesaria para crear lo que se denomi-
nan single page applications, es decir, una aplicación web de una sola
página. Esta página contiene el HTML, las CSS y el código javascript
necesario para interaccionar con los datos obtenidos desde el servidor
y mostrarlos a través de las vistas.
Imagina una aplicación de agenda como la que he dibujado más abajo.
En la vista principal se muestra la lista completa de contactos (sin
filtrar). Junto a cada uno de los contactos, tenemos un botón para
editar la información del contacto. Al pulsarlo, se muestra una nueva
página con la información del contacto.
33
8 SINGLE PAGE APPLICATIONS (APLICACIONES DE UNA SOLA
PÁGINA)
En una aplicación tradicional, el cliente solicita la página web al ser-
vidor, donde se procesa la petición. Inicialmente, se devuelve una lista
de todos los contactos. Cuando el cliente pulsa sobre un contacto para
editar los datos relacionados, se realiza la petición al servidor, que ob-
tiene los datos relativos al contacto seleccionado, se combinan con la
plantilla adecuada y se devuelven al cliente. Como todo el procesamien-
to de los datos se realiza en el servidor, cada vez que hay que tratar con
los datos, hay que contactar de nuevo con el servidor.
34
8 SINGLE PAGE APPLICATIONS (APLICACIONES DE UNA SOLA
PÁGINA)
En una aplicación con Backbone.js, los datos se procesan en el cliente
(el navegador): al solicitar la página, el servidor devuelve el documen-
to HTML que contiene todas las plantillas que necesitan las vistas de
la aplicación junto con los datos. Cuando el cliente interacciona con
la aplicación, no es necesario volver a contactar con el servidor, sino
simplemente cambiar la vista, filtrando los datos adecuados:
35
8 SINGLE PAGE APPLICATIONS (APLICACIONES DE UNA SOLA
PÁGINA)
El problema es que todas las vistas de la aplicación comparten la URL,
por lo que hay que proporcionar algún método para que el usuario
pueda acceder a una determinada funcionalidad de la aplicación web
(por ejemplo, a la visualización de un contacto determinado). Usando
REST esta URL será de la forma http://agenda.me/contacto/1, por
ejemplo. El problema es que esta URL no existe. Sin embargo, podemos
crear un enlace similar mediante el uso de una hash url; utilizando el
signo de la almohadilla # indicamos al navegador que el enlace apunta
a un fragmento de la página web, por lo que conseguimos URLs váli-
das como http://agenda.me/#contacto/1. Otro problema relaciona-
do con estas aplicaciones de una sóla página es que todos los enlaces
con hash urls no eran indexados por los buscadores. Pero desde hace
un tiempo los buscadores aceptan una especificación que permite inde-
xar todas estas URLs virtuales, por llamarlas de algún modo. Si quieres
que los buscadores indexen los enlaces de tu aplicación web, utiliza la
combinación #!. Puedes completar información al respecto échandole
un vistazo a https://developers.google.com/webmasters/ajax-crawling/
.
36
9 ROUTERS
9. Routers
Para solucionar el problema de las URLs relacionados con la single
page applications Backbone.js proporciona los router.
Hasta la llegada de la API History había que utilizar las hash urls (p.ej.
agenda.me/#edit) para proporcionar al usuario enlaces que pudieran
ser enlazables, bookmarkables, etc.
El problema es que para que un navegador pase de la URL agenda.me/contacto/1
a agenda.me/contacto/2, se requiere que se haga una petición al ser-
vidor y que este devuelva el contenido de la URL agenda.me/contacto/2.
Este proceso, llamado navegación actualiza la historia del navegador.
Así podemos pulsar los botones atrás/adelante del navegador para
avanzar/retroceder por el historial de navegación.
Con las urls fragmentadas, cuando el navegador pasa de agenda.me/#contacto/1
a agenda.me/#contacto/2 no se produce un cambio de página, sino
un cambio en la parte que se visualiza de una misma página. No hay
llamada al servidor y por tanto, no hay actualización de la historia de
navegación.
Con la llegada de este API, las aplicaciones web pueden manipular la
historia del navegador, de manera que un cambio de una URL a otra
-aunque sea una hash url- sí se interpreta como una navegación entre
páginas y, por tanto, queda registrado en la historia del navegador. De
esta forma las urls fragmentadas pasan a comportarse como urls de
verdad y pueden utilizarse para enlazar, para añadir a favoritos, para
compartir por mail, etc.
Backbone.js proporciona la clase Backbone.router para gestionar es-
tos cambios de URLs en las aplicaciones del lado del cliente y asociarlas
a acciones y eventos. Además, para los navegadores que no soportan el
API de la historia de navegación el router se degrada de forma trans-
parente a la versión hash de la URL.
La creación de un router se realiza mediante la extensión de Backbone.Router,
como es habitual en Backbone.js.
Dentro de nuestra instancia de router definimos el conjunto de urls
internas -llamadas rutas- de la aplicación y las funciones a las que
37
9 ROUTERS
deben llamar cuando se navegue a una url determinada. En estas URLs
podemos incluir una o varias partes variables, que será interpretadas
como parámetros:
var Agenda = Backbone.Router.extend({
routes : {
"ayuda" : "ayuda",
"busca/:consulta" : "busca"
},
ayuda: function(){
...
},
busca: function( consulta ){
...
}
});
En el ejemplo anterior he definido dos rutas; la primera corresponde a
miagenda.#ayuda corresponde a una ruta básica. La aplicación mapea
la ruta agenda.me/#ayuda con la función ayuda.
En el segundo caso hemos definido la ruta añadiendo :consulta; de
esta manera indicamos que este fragmento de la URL es un paráme-
tro y que debe ser asignado a la variable consulta, que pasamos a la
función correspondiente. Es decir, agenda.me/#busca/antonio resul-
ta en busca( "antonio" ); en la aplicación.
Con estos dos tipos de rutas tienes las opciones más comunes para de-
finir rutas. De todas formas, consulta la documentación en Backbone.js/#Router
para descubrir otras formas de pasar parámetros desde las URLs a las
funciones de tu aplicación.
Una vez creadas y configuradas las rutas de la aplicación web, indica-
mos a Backbone.js que monitorice los eventos de cambio de hash urls
y ¡listo!
Backbone.history.start();
38
9 ROUTERS
Podemos pasar opciones a start, por ejemplo, si queremos usar pushState
o si la página web no es servida directamente desde la raíz del do-
minio... Como siempre, es recomendable que mires la documentación
oficial completar información sobre History.
Una vez arrancada la monitorización de cambios en la url, Backbone.js
se encarga de actualizar el historial del navegador de forma automática.
Si desde la aplicación web queremos forzar la navegación de un punto
a otro de la aplicación, utilizamos el método navigate de router:
...
this.navigate( "pagina/" + numPagina );
...
Si la url a la que estamos navegando forma parte de las rutas definidas,
es probable que queramos que se dispare la función asociada definida
en la ruta; para ello, pasamos {trigger:true} a navigate:
routes: {
"ayuda/faq" : "faqMsg"
},
faqMsg: function(){
alert( ’Gracias por consultar las preguntas frecuentes’ );
}
...
agenda.navigate( "ayuda/faq", {trigger: true} );
En resumen, definimos unas rutas en Backbone.Router, que mapea-
mos a funciones relacionadas con las acciones que lleva a cabo la apli-
cación. Con Backbone.history.start() la aplicación reacciona au-
tomáticamente a los cambios de url, actualizando el historial de nave-
gación. Si desde la aplicación queremos simular una de estas navega-
ciones, usamos el método navigate.
39
10 COMUNICACIÓN CON EL SERVIDOR
10. Comunicación con el servidor
Una de las ventajas de Backbone es que reduce las peticiones necesa-
rias al servidor. En su lugar, Backbone utiliza las vistas para mostrar
únicamente la información necesaria en la vista actual.
Los datos de una aplicación web se almacenan en lo que se llama la ca-
pa de persistencia, que normalmente es una base de datos. Esta base
de datos se encuentra en el servidor, por lo que antes o después, desde
la aplicación web hay que establecer comunicación con el servidor para
actualizar datos. Backbone proporciona toda la infraestructura nece-
saria para realizar la comunicación entre el navegador y el servidor de
forma sencilla.
Primero comentaré lo que pasa en general, usando únicamente REST.
Después, veremos cómo Backbone ejecuta estos comandos a través del
módulo Backbone.sync. sync es un mecanismo de bajo nivel, dentro
del framework; en el día a día del desarrollo de la aplicación web uti-
lizaremos otros métodos propios de modelos, colecciones y vistas, que
son los que internamente utilizan sync y que comentaré al final de esta
sección.
Siguiendo con el ejemplo de la agenda, durante la primera conexión
Backbone descarga la información de todos los contactos. Después, si
el usuario consulta el contacto 1, muestra una vista de detalle con los
datos filtrados para ése único usuario, sin necesidad de ponerse en
contacto con el servidor de nuevo. A continuación vamos a entrar en
detalle sobre cómo se comunica Backbone para obtener y actualizar la
información.
En la documentación oficial de Backbone dice que proporciona un in-
terfaz REST completo a través de JSON (traducción libre de : [...] and
connects it all to your existing API over a RESTful JSON interface). ¿Qué
quiere decir ésto?. En la página en castellano de la Wikipedia encon-
tramos la definición del significado de REST, de donde destaca:
protocolo cliente/servidor sin estado: cada mensaje intercambia-
do contiene toda la información necesaria para comprender la pe-
tición.
40
10 COMUNICACIÓN CON EL SERVIDOR
un conjunto de operaciones bien definidas: aunque incluye otras,
en la práctica se utilizan sólo cuatro GET, POST, PUT, DELETE.
sintaxis universal que permite identificar los recursos a través de
su URI.
Con estas ideas en mente, ahora volvemos a la aplicación de la agenda.
Cuando el usuario accede a http://agenda.me/, es decir a la raíz
de nuestra aplicación /, seguramente nos interese definir una route
hacia /contactos/. Como no especificamos ningún id de contacto,
si lo hemos programado así, nuestro servidor nos devolverá todos los
contactos almacenados en la base de datos (mediante GET).
Cuando el usuario introduce un nuevo contacto, enviamos la informa-
ción al servidor mediante POST; el servidor se encarga de asignar el
siguiente id libre en el equivalente en nuestra base de datos de la lista
de /contactos.
Si el usuario quiere acceder a un contacto concreto, como el que tiene el
id=5, en este caso el identificador (URI del recurso) será: /contactos/5,
de nuevo, usando GET.
Mediante DELETE indicamos que vamos a borrar un contacto. Para ello
debemos aportar el id del contacto a borrar; por ejemplo, como antes
/contactos/5.
Finalmente, usamos PUT para actualizar la información de un contacto
(o crear uno nuevo). Como es lógico, aportamos el id del contacto que
queremos acutalizar junto con la información actualizada.
Backbone proporciona toda esta funcionalidad a través de Backbone.sync.
sync toma tres parámetros: method, model y options (este último es
opcional). El method es uno de los cuatro verbos de REST: GET, POST,
PUT, DELETE. El segundo, model es el modelo a guardar en el servidor
(o la colección que vamos a leer); Backbone interpreta qué tiene que
hacer con los datos que le pasamos.
Enlazando con el ejemplo anterior de la agenda, por defecto sync sería
algo así:
crear nuevo contacto : POST →/contactos
41
10 COMUNICACIÓN CON EL SERVIDOR
leer (contacto o colección): GET → /contactos[/id] Si no se pro-
porciona un id, es que queremos leer toda la colección.
actualiza un contacto : PUT → /contactos/id
borrar contacto : DELETE → /contactos/id
Aunque Backbone.sync usa jQuery.ajax(), en realidad podemos sus-
tituirlo por cualquier otro método que nos interese, en función de nues-
tras necesidades. Un ejemplo de ello es la aplicación localtodos, que
usa Backbone.localStorage. localStorage funciona como un plu-
gin que usa el local storage (almacenamiento local) de HTML5 en vez de
un servidor de bases de datos como capa de almacenamiento.
Como he comentado al principio de esta sección, sync es un meca-
nismo interno de Backbone; al desarrollar la aplicación web rara vez
tendremos que llamar a sync de manera manual.
De nuevo volvemos al ejemplo de la agenda. La primera vez que el
usuario accede a la aplicación web crearemos una colección Contactos
donde almacenar localmente la información obtenida del servidor. En
la definición de la colección habremos incluido la propiedad url, que
indica a Backbone dónde buscar y almacenar los datos contenidos en
la colección. En la sección sobre Colecciones de este tutorial verás que
utilicé url: ’#’ para evitar que Backbone se intentara comunicar con
una url externa a la página.
Ahora, suponiendo que ya tenemos el servidor configurado, nuestra
colección incluiría:
Contactos = Backbone.Collection.extend({
Model: Contacto,
url: "/contactos/"
});
Así la colección sabe dónde están almacenados los datos con los que
se relaciona. Por tanto, en la función de inicialización de la colección
podemos incluir:
42
10 COMUNICACIÓN CON EL SERVIDOR
initialize: function(){
...
Contactos.fetch();
...
};
fetch obtiene los datos almacenados en la url de la colección en el
servidor. En nuestro ejemplo de la agenda, obtendría todos los datos
almacenados en la base de datos, pasándolos a una vista y mostrándo-
los al usuario al visitar la página por primera vez. Incluso si llamamos
a fetch en respuesta a algún evento que se produce en la aplicación,
Backbone utiliza set para fusionar los datos locales con los obteni-
dos del servidor de forma inteligente (a no ser que utilicemos {reset:
true}). En las opciones de fetch podemos espeficificar funciones de
callback para los eventos success y error de comunicación con el ser-
vidor, actuando en consecuencia. A estas funciones Backbone les pasa
automáticamente collection, response y options como argumen-
tos. De hecho, fetch delega su ejecución a Backbone.sync de forma
transparente (que a su vez utiliza jQuery.ajax() para realizar la co-
municación con el servidor).
fetch hace que se disparen los eventos add para cada modelo añadido
y change para cada modelo cambiado. Así, si hemos especificado que
ocurran cosas cuando cambien los modelos -por ejemplo, actualizar
una vista- se ejecutará el código adecuado y todo funcionará sin que
tengamos que preocuparnos por ello.
fetch puede llamarse tanto para modelos como para colecciones. Si
url no está definido para el modelo, utiliza url de la colección asocia-
da. El único requerimiento para fetch es que el servidor devuelva un
array de modelos en formato JSON.
Volviendo la vista atrás un momento, tenemos que, lo que anteriormen-
te habíamos comentado:
leer (contacto o colección): GET → /contactos[/id] Si no se pro-
porciona un id, es que queremos leer toda la colección.
sería, mediante fetch:
43
10 COMUNICACIÓN CON EL SERVIDOR
leer (contacto o colección):
Contactos.fetch()
(toda la colección) o
Contacto.fetch()
(para obtener un contacto en concreto).
Al obtener el contacto no tenemos que especificar el id del modelo, ya
que internamente Backbone sabe cuál es el id asociado a cada modelo
de una colección, y no hace falta que lo especifiquemos nosotros.
Para el resto de operaciones de creación, actualización o eliminación
de modelos, tal y como hemos hecho con fetch utilizamos funciones
específicas en vez de utilizar Backbone.sync a pelo.
Backbone proporciona el método save para guardar un nuevo modelo
en la base de datos o para actualizar uno existente.
Contacto = Backbone.Model.extend();
var contacto = new Contacto({
nombre: ’Anthony Machine’,
telefono: ’+34931234567’
});
contacto.save(); // Lo guarda en el servidor
El siguiente trozo de código (adaptado de la página de Backbone) mues-
tra una función sync modificada, que muestra un alert() en vez de
comunicarse con el servidor. Al ejecutar el código por primera vez, save
envía el modelo completo, ya que no existe inicialmente. En la segun-
da llamada a save, especificamos sólo la parte de la información del
contacto actualizada; Backbone te facilita el trabajo y no es necesario
utilizar funciones diferentes para crear y actualizar ni es necesario es-
cribir código para comprobar si un registro existe antes de modificarlo
y cosas por el estilo...
44
10 COMUNICACIÓN CON EL SERVIDOR
Backbone.sync = function(method, model) {
alert(method + ": " + JSON.stringify(model));
model.id = 1;
};
var contacto = new Backbone.Model({
nombre: "Anthony Machine",
telefono: "+34931234567"
});
// Primera llamada : crea el contacto
contacto.save();
// Segunda llamada : actualiza el contacto ( que ya existe )
contacto.save({nombre: "Federico Mercurio"});
En la primera llamada:
Y en la segunda:
45
10 COMUNICACIÓN CON EL SERVIDOR
Para evitar enviar el modelo completo, podemos utilizar el parámetro
{patch: true} que envía sólo los atributos modificados al servidor,
optimizando las comunicaciones. Backbone utiliza isNew() para de-
terminar si el modelo no existe -con lo que envía un HTTP POST, para
crearlo- o si estamos haciendo una actualización, con lo que envía HTTP
PUT.
Finalmente, para eliminar un modelo del servidor, usaremos destroy(),
lo que genera una petición HTTP DELETE.
var contacto = new Backbone.Model({
nombre: "Justino Biever",
telefono: "+34666234567"
});
// Nos deshacemos del contacto
contacto.destroy();
Obviamente, estas acciones están asociadas a los modelos que vamos
a modificar. Si queremos llevar a cabo estas acciones (por ejemplo, eli-
minar) para toda una colección, utilizamos el método de underscore.js
_.each() para actuar sobre cada modelo de la colección.
46
10 COMUNICACIÓN CON EL SERVIDOR
En esta sección he intentado clarificar un poco cómo gestiona Back-
bone la comunicación del servidor. Backbone proporciona métodos es-
pecíficos para modelos y/o colecciones que permiten realizar las ope-
raciones básicas de manipulación de modelos y colecciones: fetch(),
save(), destroy(), equivalentes a las operaciones CRUD (Crear, leer
(read), actualizar (update) y destruir). Backbone es suficientemente
inteligente como para distinguir cuándo se trata de crear o actua-
lizar un modelo. Internamente estos métodos específicos delegan en
Backbone.sync. Por defecto, sync usa la función .ajax() de jQuery
(o de Zepto) para comunicarse con el servidor, aunque podemos cam-
biarlo fácilmente.
Además de proporcionar la infraestructura necesaria para realizar la
comunicación con el servidor, Backbone también se encarga de dispa-
rar eventos que permiten a otras partes de nuestra aplicación reaccio-
nar ante los cambios que se produzcan en los modelos, independiente-
mente de su origen.
47

Más contenido relacionado

Destacado

Amoura's sura.vol.2.cardstocc
Amoura's sura.vol.2.cardstoccAmoura's sura.vol.2.cardstocc
Amoura's sura.vol.2.cardstoccMurad Wysinger
 
31_Dean's_List_Fall2014
31_Dean's_List_Fall201431_Dean's_List_Fall2014
31_Dean's_List_Fall2014Husam Alghamdi
 
Geospatial Careers: Getting a Job [2015]
Geospatial Careers: Getting a Job [2015]Geospatial Careers: Getting a Job [2015]
Geospatial Careers: Getting a Job [2015]Donna Genzmer
 
Ashford University Transcript
Ashford University TranscriptAshford University Transcript
Ashford University TranscriptSteven Wanda
 
07_Safe_Work_Practices
07_Safe_Work_Practices07_Safe_Work_Practices
07_Safe_Work_PracticesHusam Alghamdi
 
Eric Brouman Letter of Rec RTH
Eric Brouman Letter of Rec RTHEric Brouman Letter of Rec RTH
Eric Brouman Letter of Rec RTHEric Brouman
 
التحفة الشدوية لعمل الاستبيانات الإلكترونية
التحفة الشدوية لعمل الاستبيانات الإلكترونيةالتحفة الشدوية لعمل الاستبيانات الإلكترونية
التحفة الشدوية لعمل الاستبيانات الإلكترونيةalshadwi
 
الفرق بين معالجات انتل Core i3, i5, i7
الفرق بين معالجات انتل Core i3, i5, i7الفرق بين معالجات انتل Core i3, i5, i7
الفرق بين معالجات انتل Core i3, i5, i7alshadwi
 
Redacción eficaz con enfoque jurídico taller de redactores asociados tareas
Redacción eficaz con enfoque jurídico taller de redactores asociados tareasRedacción eficaz con enfoque jurídico taller de redactores asociados tareas
Redacción eficaz con enfoque jurídico taller de redactores asociados tareasTaller de Redactores Asociados TAREAS
 
OpenQuake: scienza aperta per il rischio sismico
OpenQuake: scienza aperta per il rischio sismicoOpenQuake: scienza aperta per il rischio sismico
OpenQuake: scienza aperta per il rischio sismicoPaul Henshaw
 
Enseñar y aprender por competencias II
Enseñar y aprender por competencias IIEnseñar y aprender por competencias II
Enseñar y aprender por competencias IIAlfredo Hernando
 
Music & Society Jazz History in brief
Music & Society Jazz History in briefMusic & Society Jazz History in brief
Music & Society Jazz History in briefChristopher Baker
 
Aprendizaje en linea
Aprendizaje en lineaAprendizaje en linea
Aprendizaje en lineaverano97
 

Destacado (18)

Amoura's sura.vol.2.cardstocc
Amoura's sura.vol.2.cardstoccAmoura's sura.vol.2.cardstocc
Amoura's sura.vol.2.cardstocc
 
Learning devise
Learning deviseLearning devise
Learning devise
 
31_Dean's_List_Fall2014
31_Dean's_List_Fall201431_Dean's_List_Fall2014
31_Dean's_List_Fall2014
 
Geospatial Careers: Getting a Job [2015]
Geospatial Careers: Getting a Job [2015]Geospatial Careers: Getting a Job [2015]
Geospatial Careers: Getting a Job [2015]
 
Raton5
Raton5Raton5
Raton5
 
Ashford University Transcript
Ashford University TranscriptAshford University Transcript
Ashford University Transcript
 
07_Safe_Work_Practices
07_Safe_Work_Practices07_Safe_Work_Practices
07_Safe_Work_Practices
 
Evolucionismo blabla
Evolucionismo blablaEvolucionismo blabla
Evolucionismo blabla
 
Eric Brouman Letter of Rec RTH
Eric Brouman Letter of Rec RTHEric Brouman Letter of Rec RTH
Eric Brouman Letter of Rec RTH
 
التحفة الشدوية لعمل الاستبيانات الإلكترونية
التحفة الشدوية لعمل الاستبيانات الإلكترونيةالتحفة الشدوية لعمل الاستبيانات الإلكترونية
التحفة الشدوية لعمل الاستبيانات الإلكترونية
 
الفرق بين معالجات انتل Core i3, i5, i7
الفرق بين معالجات انتل Core i3, i5, i7الفرق بين معالجات انتل Core i3, i5, i7
الفرق بين معالجات انتل Core i3, i5, i7
 
Redacción eficaz con enfoque jurídico taller de redactores asociados tareas
Redacción eficaz con enfoque jurídico taller de redactores asociados tareasRedacción eficaz con enfoque jurídico taller de redactores asociados tareas
Redacción eficaz con enfoque jurídico taller de redactores asociados tareas
 
OpenQuake: scienza aperta per il rischio sismico
OpenQuake: scienza aperta per il rischio sismicoOpenQuake: scienza aperta per il rischio sismico
OpenQuake: scienza aperta per il rischio sismico
 
Registro mercantil
Registro mercantilRegistro mercantil
Registro mercantil
 
Enseñar y aprender por competencias II
Enseñar y aprender por competencias IIEnseñar y aprender por competencias II
Enseñar y aprender por competencias II
 
Music & Society Jazz History in brief
Music & Society Jazz History in briefMusic & Society Jazz History in brief
Music & Society Jazz History in brief
 
3D printing
3D printing3D printing
3D printing
 
Aprendizaje en linea
Aprendizaje en lineaAprendizaje en linea
Aprendizaje en linea
 

Similar a Primeros pasos con Backbone js, por Xavier Aznar

Php Bitter Sweet Symfony!
Php Bitter Sweet Symfony!Php Bitter Sweet Symfony!
Php Bitter Sweet Symfony!Ricard Luquero
 
Manual de netbeans_7(2)(2)(2)
Manual de netbeans_7(2)(2)(2)Manual de netbeans_7(2)(2)(2)
Manual de netbeans_7(2)(2)(2)javier_ot99
 
Tutorial aprendiendo a programar
Tutorial aprendiendo a programarTutorial aprendiendo a programar
Tutorial aprendiendo a programarEduardo Méndez
 
Tutorial aprendiendo a programar
Tutorial aprendiendo a programarTutorial aprendiendo a programar
Tutorial aprendiendo a programarJuan Hoyos
 
Tutorial aprendiendo a programar
Tutorial aprendiendo a programarTutorial aprendiendo a programar
Tutorial aprendiendo a programarblasty2
 
Tutorial aprendiendo a programar
Tutorial aprendiendo a programarTutorial aprendiendo a programar
Tutorial aprendiendo a programarblasty2
 
Bases de datos_angelina_monetti
Bases de datos_angelina_monettiBases de datos_angelina_monetti
Bases de datos_angelina_monettiangelinamonetti1
 
bases de datos desde visual basic
bases de datos desde visual basicbases de datos desde visual basic
bases de datos desde visual basicsantiagomario8
 
Conectar con bases de datos
Conectar con bases de datosConectar con bases de datos
Conectar con bases de datosRafael Quintero
 
A toda máquina con herencia visual
A toda máquina con herencia visualA toda máquina con herencia visual
A toda máquina con herencia visualIgnacio Monllor
 
Base de datos desde vb 6.0
Base de datos desde vb 6.0Base de datos desde vb 6.0
Base de datos desde vb 6.0WendyMendez30
 
C sharp manual[1]
C sharp manual[1]C sharp manual[1]
C sharp manual[1]Kalizbeth
 
Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6Gabriela Bosetti
 
Conexion mysql con java usando netbeans
Conexion mysql con java usando netbeansConexion mysql con java usando netbeans
Conexion mysql con java usando netbeansEmerson Garay
 

Similar a Primeros pasos con Backbone js, por Xavier Aznar (20)

Php Bitter Sweet Symfony!
Php Bitter Sweet Symfony!Php Bitter Sweet Symfony!
Php Bitter Sweet Symfony!
 
Manual de netbeans_7(2)(2)(2)
Manual de netbeans_7(2)(2)(2)Manual de netbeans_7(2)(2)(2)
Manual de netbeans_7(2)(2)(2)
 
Tutorial aprendiendo a programar
Tutorial aprendiendo a programarTutorial aprendiendo a programar
Tutorial aprendiendo a programar
 
Tutorial aprendiendo a programar
Tutorial aprendiendo a programarTutorial aprendiendo a programar
Tutorial aprendiendo a programar
 
Tutorial aprendiendo a programar
Tutorial aprendiendo a programarTutorial aprendiendo a programar
Tutorial aprendiendo a programar
 
Tutorial aprendiendo a programar
Tutorial aprendiendo a programarTutorial aprendiendo a programar
Tutorial aprendiendo a programar
 
Mvc
MvcMvc
Mvc
 
Bases de datos_angelina_monetti
Bases de datos_angelina_monettiBases de datos_angelina_monetti
Bases de datos_angelina_monetti
 
Tutorial net beans
Tutorial net beansTutorial net beans
Tutorial net beans
 
bases de datos desde visual basic
bases de datos desde visual basicbases de datos desde visual basic
bases de datos desde visual basic
 
Conectar con bases de datos
Conectar con bases de datosConectar con bases de datos
Conectar con bases de datos
 
Practica adicional
Practica adicionalPractica adicional
Practica adicional
 
A toda máquina con herencia visual
A toda máquina con herencia visualA toda máquina con herencia visual
A toda máquina con herencia visual
 
Base de datos desde vb 6.0
Base de datos desde vb 6.0Base de datos desde vb 6.0
Base de datos desde vb 6.0
 
C sharp manual
C sharp manualC sharp manual
C sharp manual
 
C sharp manual
C sharp manualC sharp manual
C sharp manual
 
C sharp manual[1]
C sharp manual[1]C sharp manual[1]
C sharp manual[1]
 
C sharp manual
C sharp manualC sharp manual
C sharp manual
 
Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6
 
Conexion mysql con java usando netbeans
Conexion mysql con java usando netbeansConexion mysql con java usando netbeans
Conexion mysql con java usando netbeans
 

Más de Pablo Aguilera

Billetes de España (1925-1992)
Billetes de España (1925-1992)Billetes de España (1925-1992)
Billetes de España (1925-1992)Pablo Aguilera
 
Revista La Gatera de la Villa, nº 3
Revista La Gatera de la Villa, nº 3Revista La Gatera de la Villa, nº 3
Revista La Gatera de la Villa, nº 3Pablo Aguilera
 
Revista La Gatera de la Villa, nº 2
Revista La Gatera de la Villa, nº 2Revista La Gatera de la Villa, nº 2
Revista La Gatera de la Villa, nº 2Pablo Aguilera
 
Secuencias de Sigüenza
Secuencias de SigüenzaSecuencias de Sigüenza
Secuencias de SigüenzaPablo Aguilera
 
Revista "El estornino de Mozart", marzo 2013
Revista "El estornino de Mozart", marzo 2013Revista "El estornino de Mozart", marzo 2013
Revista "El estornino de Mozart", marzo 2013Pablo Aguilera
 
Revista "El estornino de Mozart", febrero 2013
Revista "El estornino de Mozart", febrero 2013Revista "El estornino de Mozart", febrero 2013
Revista "El estornino de Mozart", febrero 2013Pablo Aguilera
 
Revista "El estornino de Mozart", enero 2013
Revista "El estornino de Mozart", enero 2013Revista "El estornino de Mozart", enero 2013
Revista "El estornino de Mozart", enero 2013Pablo Aguilera
 
Primeras jornadas madrileñas de novela historica
Primeras jornadas madrileñas de novela historicaPrimeras jornadas madrileñas de novela historica
Primeras jornadas madrileñas de novela historicaPablo Aguilera
 
25 trucos caseros que te van a hacer la vida más simple
25 trucos caseros que te van a hacer la vida más simple25 trucos caseros que te van a hacer la vida más simple
25 trucos caseros que te van a hacer la vida más simplePablo Aguilera
 
Aquellos que fuimos niños en los 60, 70 u 80
Aquellos que fuimos niños en los 60, 70 u 80Aquellos que fuimos niños en los 60, 70 u 80
Aquellos que fuimos niños en los 60, 70 u 80Pablo Aguilera
 
A lapiz. Dirk Dzimirsky
A lapiz. Dirk DzimirskyA lapiz. Dirk Dzimirsky
A lapiz. Dirk DzimirskyPablo Aguilera
 
Mucho más que imágenes
Mucho más que imágenesMucho más que imágenes
Mucho más que imágenesPablo Aguilera
 
Me gustan estas frases, ¿y a ti?
Me gustan estas frases, ¿y a ti?Me gustan estas frases, ¿y a ti?
Me gustan estas frases, ¿y a ti?Pablo Aguilera
 
Crea tu propio zoo sin salir de casa
Crea  tu propio zoo sin salir de casaCrea  tu propio zoo sin salir de casa
Crea tu propio zoo sin salir de casaPablo Aguilera
 
Revista Proiectus nº 3
Revista Proiectus nº 3Revista Proiectus nº 3
Revista Proiectus nº 3Pablo Aguilera
 
Manual del pintor de historia, ó sea recopilación de las principales reglas, ...
Manual del pintor de historia, ó sea recopilación de las principales reglas, ...Manual del pintor de historia, ó sea recopilación de las principales reglas, ...
Manual del pintor de historia, ó sea recopilación de las principales reglas, ...Pablo Aguilera
 
Rincones curiosos de España
Rincones curiosos de EspañaRincones curiosos de España
Rincones curiosos de EspañaPablo Aguilera
 
Guía de juegos tradicionales madrileños
Guía de juegos tradicionales madrileñosGuía de juegos tradicionales madrileños
Guía de juegos tradicionales madrileñosPablo Aguilera
 

Más de Pablo Aguilera (20)

Billetes de España (1925-1992)
Billetes de España (1925-1992)Billetes de España (1925-1992)
Billetes de España (1925-1992)
 
Revista La Gatera de la Villa, nº 3
Revista La Gatera de la Villa, nº 3Revista La Gatera de la Villa, nº 3
Revista La Gatera de la Villa, nº 3
 
Revista La Gatera de la Villa, nº 2
Revista La Gatera de la Villa, nº 2Revista La Gatera de la Villa, nº 2
Revista La Gatera de la Villa, nº 2
 
Frutos en flor
Frutos en florFrutos en flor
Frutos en flor
 
Secuencias de Sigüenza
Secuencias de SigüenzaSecuencias de Sigüenza
Secuencias de Sigüenza
 
Revista "El estornino de Mozart", marzo 2013
Revista "El estornino de Mozart", marzo 2013Revista "El estornino de Mozart", marzo 2013
Revista "El estornino de Mozart", marzo 2013
 
Revista "El estornino de Mozart", febrero 2013
Revista "El estornino de Mozart", febrero 2013Revista "El estornino de Mozart", febrero 2013
Revista "El estornino de Mozart", febrero 2013
 
Revista "El estornino de Mozart", enero 2013
Revista "El estornino de Mozart", enero 2013Revista "El estornino de Mozart", enero 2013
Revista "El estornino de Mozart", enero 2013
 
Primeras jornadas madrileñas de novela historica
Primeras jornadas madrileñas de novela historicaPrimeras jornadas madrileñas de novela historica
Primeras jornadas madrileñas de novela historica
 
25 trucos caseros que te van a hacer la vida más simple
25 trucos caseros que te van a hacer la vida más simple25 trucos caseros que te van a hacer la vida más simple
25 trucos caseros que te van a hacer la vida más simple
 
Aquellos que fuimos niños en los 60, 70 u 80
Aquellos que fuimos niños en los 60, 70 u 80Aquellos que fuimos niños en los 60, 70 u 80
Aquellos que fuimos niños en los 60, 70 u 80
 
A lapiz. Dirk Dzimirsky
A lapiz. Dirk DzimirskyA lapiz. Dirk Dzimirsky
A lapiz. Dirk Dzimirsky
 
Mucho más que imágenes
Mucho más que imágenesMucho más que imágenes
Mucho más que imágenes
 
Praga nevada de noche
Praga nevada de nochePraga nevada de noche
Praga nevada de noche
 
Me gustan estas frases, ¿y a ti?
Me gustan estas frases, ¿y a ti?Me gustan estas frases, ¿y a ti?
Me gustan estas frases, ¿y a ti?
 
Crea tu propio zoo sin salir de casa
Crea  tu propio zoo sin salir de casaCrea  tu propio zoo sin salir de casa
Crea tu propio zoo sin salir de casa
 
Revista Proiectus nº 3
Revista Proiectus nº 3Revista Proiectus nº 3
Revista Proiectus nº 3
 
Manual del pintor de historia, ó sea recopilación de las principales reglas, ...
Manual del pintor de historia, ó sea recopilación de las principales reglas, ...Manual del pintor de historia, ó sea recopilación de las principales reglas, ...
Manual del pintor de historia, ó sea recopilación de las principales reglas, ...
 
Rincones curiosos de España
Rincones curiosos de EspañaRincones curiosos de España
Rincones curiosos de España
 
Guía de juegos tradicionales madrileños
Guía de juegos tradicionales madrileñosGuía de juegos tradicionales madrileños
Guía de juegos tradicionales madrileños
 

Último

institucion educativa la esperanza sede magdalena
institucion educativa la esperanza sede magdalenainstitucion educativa la esperanza sede magdalena
institucion educativa la esperanza sede magdalenajuniorcuellargomez
 
libro de Ciencias Sociales_6to grado.pdf
libro de Ciencias Sociales_6to grado.pdflibro de Ciencias Sociales_6to grado.pdf
libro de Ciencias Sociales_6to grado.pdfFAUSTODANILOCRUZCAST
 
GRUPO 10 SOFTWARE DE EL CAMPO DE LA SAULD
GRUPO 10 SOFTWARE DE EL CAMPO DE LA SAULDGRUPO 10 SOFTWARE DE EL CAMPO DE LA SAULD
GRUPO 10 SOFTWARE DE EL CAMPO DE LA SAULDLeslie Villar
 
rodriguez_DelAngel_MariaGPE_M1S3AL6.pptx
rodriguez_DelAngel_MariaGPE_M1S3AL6.pptxrodriguez_DelAngel_MariaGPE_M1S3AL6.pptx
rodriguez_DelAngel_MariaGPE_M1S3AL6.pptxssuser61dda7
 
actividad.06_crea_un_recurso_multimedia_M01_S03_M01.ppsx
actividad.06_crea_un_recurso_multimedia_M01_S03_M01.ppsxactividad.06_crea_un_recurso_multimedia_M01_S03_M01.ppsx
actividad.06_crea_un_recurso_multimedia_M01_S03_M01.ppsx241532171
 
Institucion educativa la esperanza sede la magdalena
Institucion educativa la esperanza sede la magdalenaInstitucion educativa la esperanza sede la magdalena
Institucion educativa la esperanza sede la magdalenadanielaerazok
 
Buscadores, SEM SEO: el desafío de ser visto en la web
Buscadores, SEM SEO: el desafío de ser visto en la webBuscadores, SEM SEO: el desafío de ser visto en la web
Buscadores, SEM SEO: el desafío de ser visto en la webDecaunlz
 
INSTITUCION EDUCATIVA LA ESPERANZA SEDE MAGDALENA
INSTITUCION EDUCATIVA LA ESPERANZA SEDE MAGDALENAINSTITUCION EDUCATIVA LA ESPERANZA SEDE MAGDALENA
INSTITUCION EDUCATIVA LA ESPERANZA SEDE MAGDALENAdanielaerazok
 
GRUPO 5 Software en el campo de la salud.pptx
GRUPO 5 Software en el campo de la salud.pptxGRUPO 5 Software en el campo de la salud.pptx
GRUPO 5 Software en el campo de la salud.pptxNicolas Villarroel
 
PRIMARIA 1. RESUELVE PROBLEMAS DE FORMA MOVIMIENTO Y LOCALIZACIÓN 2 (2).pptx
PRIMARIA 1. RESUELVE PROBLEMAS DE FORMA MOVIMIENTO Y LOCALIZACIÓN 2 (2).pptxPRIMARIA 1. RESUELVE PROBLEMAS DE FORMA MOVIMIENTO Y LOCALIZACIÓN 2 (2).pptx
PRIMARIA 1. RESUELVE PROBLEMAS DE FORMA MOVIMIENTO Y LOCALIZACIÓN 2 (2).pptxRodriguezLucero
 
Producto académico 03 - Habilidades Comunicativas.pptx
Producto académico 03 - Habilidades Comunicativas.pptxProducto académico 03 - Habilidades Comunicativas.pptx
Producto académico 03 - Habilidades Comunicativas.pptx46828205
 
2º SOY LECTOR PART 2- MD EDUCATIVO (6).pdf
2º SOY LECTOR PART 2- MD  EDUCATIVO (6).pdf2º SOY LECTOR PART 2- MD  EDUCATIVO (6).pdf
2º SOY LECTOR PART 2- MD EDUCATIVO (6).pdfFernandaHernandez312615
 
Fisica General.pdf ESCUELA D QUIMICA E INGENIERIA
Fisica General.pdf ESCUELA D QUIMICA E INGENIERIAFisica General.pdf ESCUELA D QUIMICA E INGENIERIA
Fisica General.pdf ESCUELA D QUIMICA E INGENIERIAcoloncopias5
 
FLUIDEZ-Teatro-Leido-4to-Grado-El-leon-y-el-raton- (1).pdf
FLUIDEZ-Teatro-Leido-4to-Grado-El-leon-y-el-raton- (1).pdfFLUIDEZ-Teatro-Leido-4to-Grado-El-leon-y-el-raton- (1).pdf
FLUIDEZ-Teatro-Leido-4to-Grado-El-leon-y-el-raton- (1).pdfYuriFuentesMartinez2
 
El uso de las tic en la vida continúa , ambiente positivo y negativo.
El uso de las tic  en la vida continúa , ambiente positivo y negativo.El uso de las tic  en la vida continúa , ambiente positivo y negativo.
El uso de las tic en la vida continúa , ambiente positivo y negativo.ayalayenifer617
 
COMPETENCIAS CIUDADANASadadadadadadada .pdf
COMPETENCIAS CIUDADANASadadadadadadada .pdfCOMPETENCIAS CIUDADANASadadadadadadada .pdf
COMPETENCIAS CIUDADANASadadadadadadada .pdfOscarBlas6
 
CamposGarcia_MariaMagdalena_M1S3AI6.pptx
CamposGarcia_MariaMagdalena_M1S3AI6.pptxCamposGarcia_MariaMagdalena_M1S3AI6.pptx
CamposGarcia_MariaMagdalena_M1S3AI6.pptx241518192
 
3Mayo2023 Taller construcción de Prototipos.pptx
3Mayo2023 Taller construcción de Prototipos.pptx3Mayo2023 Taller construcción de Prototipos.pptx
3Mayo2023 Taller construcción de Prototipos.pptxadso2024sena
 
Tema 1 - Fundamentos de gestión contable.pptx
Tema 1 - Fundamentos de gestión contable.pptxTema 1 - Fundamentos de gestión contable.pptx
Tema 1 - Fundamentos de gestión contable.pptxchinojosa17
 

Último (19)

institucion educativa la esperanza sede magdalena
institucion educativa la esperanza sede magdalenainstitucion educativa la esperanza sede magdalena
institucion educativa la esperanza sede magdalena
 
libro de Ciencias Sociales_6to grado.pdf
libro de Ciencias Sociales_6to grado.pdflibro de Ciencias Sociales_6to grado.pdf
libro de Ciencias Sociales_6to grado.pdf
 
GRUPO 10 SOFTWARE DE EL CAMPO DE LA SAULD
GRUPO 10 SOFTWARE DE EL CAMPO DE LA SAULDGRUPO 10 SOFTWARE DE EL CAMPO DE LA SAULD
GRUPO 10 SOFTWARE DE EL CAMPO DE LA SAULD
 
rodriguez_DelAngel_MariaGPE_M1S3AL6.pptx
rodriguez_DelAngel_MariaGPE_M1S3AL6.pptxrodriguez_DelAngel_MariaGPE_M1S3AL6.pptx
rodriguez_DelAngel_MariaGPE_M1S3AL6.pptx
 
actividad.06_crea_un_recurso_multimedia_M01_S03_M01.ppsx
actividad.06_crea_un_recurso_multimedia_M01_S03_M01.ppsxactividad.06_crea_un_recurso_multimedia_M01_S03_M01.ppsx
actividad.06_crea_un_recurso_multimedia_M01_S03_M01.ppsx
 
Institucion educativa la esperanza sede la magdalena
Institucion educativa la esperanza sede la magdalenaInstitucion educativa la esperanza sede la magdalena
Institucion educativa la esperanza sede la magdalena
 
Buscadores, SEM SEO: el desafío de ser visto en la web
Buscadores, SEM SEO: el desafío de ser visto en la webBuscadores, SEM SEO: el desafío de ser visto en la web
Buscadores, SEM SEO: el desafío de ser visto en la web
 
INSTITUCION EDUCATIVA LA ESPERANZA SEDE MAGDALENA
INSTITUCION EDUCATIVA LA ESPERANZA SEDE MAGDALENAINSTITUCION EDUCATIVA LA ESPERANZA SEDE MAGDALENA
INSTITUCION EDUCATIVA LA ESPERANZA SEDE MAGDALENA
 
GRUPO 5 Software en el campo de la salud.pptx
GRUPO 5 Software en el campo de la salud.pptxGRUPO 5 Software en el campo de la salud.pptx
GRUPO 5 Software en el campo de la salud.pptx
 
PRIMARIA 1. RESUELVE PROBLEMAS DE FORMA MOVIMIENTO Y LOCALIZACIÓN 2 (2).pptx
PRIMARIA 1. RESUELVE PROBLEMAS DE FORMA MOVIMIENTO Y LOCALIZACIÓN 2 (2).pptxPRIMARIA 1. RESUELVE PROBLEMAS DE FORMA MOVIMIENTO Y LOCALIZACIÓN 2 (2).pptx
PRIMARIA 1. RESUELVE PROBLEMAS DE FORMA MOVIMIENTO Y LOCALIZACIÓN 2 (2).pptx
 
Producto académico 03 - Habilidades Comunicativas.pptx
Producto académico 03 - Habilidades Comunicativas.pptxProducto académico 03 - Habilidades Comunicativas.pptx
Producto académico 03 - Habilidades Comunicativas.pptx
 
2º SOY LECTOR PART 2- MD EDUCATIVO (6).pdf
2º SOY LECTOR PART 2- MD  EDUCATIVO (6).pdf2º SOY LECTOR PART 2- MD  EDUCATIVO (6).pdf
2º SOY LECTOR PART 2- MD EDUCATIVO (6).pdf
 
Fisica General.pdf ESCUELA D QUIMICA E INGENIERIA
Fisica General.pdf ESCUELA D QUIMICA E INGENIERIAFisica General.pdf ESCUELA D QUIMICA E INGENIERIA
Fisica General.pdf ESCUELA D QUIMICA E INGENIERIA
 
FLUIDEZ-Teatro-Leido-4to-Grado-El-leon-y-el-raton- (1).pdf
FLUIDEZ-Teatro-Leido-4to-Grado-El-leon-y-el-raton- (1).pdfFLUIDEZ-Teatro-Leido-4to-Grado-El-leon-y-el-raton- (1).pdf
FLUIDEZ-Teatro-Leido-4to-Grado-El-leon-y-el-raton- (1).pdf
 
El uso de las tic en la vida continúa , ambiente positivo y negativo.
El uso de las tic  en la vida continúa , ambiente positivo y negativo.El uso de las tic  en la vida continúa , ambiente positivo y negativo.
El uso de las tic en la vida continúa , ambiente positivo y negativo.
 
COMPETENCIAS CIUDADANASadadadadadadada .pdf
COMPETENCIAS CIUDADANASadadadadadadada .pdfCOMPETENCIAS CIUDADANASadadadadadadada .pdf
COMPETENCIAS CIUDADANASadadadadadadada .pdf
 
CamposGarcia_MariaMagdalena_M1S3AI6.pptx
CamposGarcia_MariaMagdalena_M1S3AI6.pptxCamposGarcia_MariaMagdalena_M1S3AI6.pptx
CamposGarcia_MariaMagdalena_M1S3AI6.pptx
 
3Mayo2023 Taller construcción de Prototipos.pptx
3Mayo2023 Taller construcción de Prototipos.pptx3Mayo2023 Taller construcción de Prototipos.pptx
3Mayo2023 Taller construcción de Prototipos.pptx
 
Tema 1 - Fundamentos de gestión contable.pptx
Tema 1 - Fundamentos de gestión contable.pptxTema 1 - Fundamentos de gestión contable.pptx
Tema 1 - Fundamentos de gestión contable.pptx
 

Primeros pasos con Backbone js, por Xavier Aznar

  • 1.
  • 2. Primeros pasos con Backbone.js Introducción a Backbone.js por Xavi Aznar Índice 1. Primeros pasos 4 1.1. Requisitos básicos . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2. Enlazando Backbone . . . . . . . . . . . . . . . . . . . . . . 4 2. Modelos 5 2.1. Nuestro primer modelo . . . . . . . . . . . . . . . . . . . . . 5 3. Colecciones 9 4. Vistas 19 5. Plantillas 25 6. Controladores y Eventos 30 7. Eventos y vistas 31 8. Single Page Applications (aplicaciones de una sola página) 33 9. Routers 37 10.Comunicación con el servidor 40 2
  • 3. ÍNDICE ÍNDICE Comentarios de la adaptación del tutorial al formato offline Este eBook es la versión offline del tutorial que se encuentra colgado en http://bit.ly/1erosPasosBackbone. El tutorial fue pensado como un bloc de notas donde recoger mis progresos con Backbone. Poco a poco se fue convirtiendo en un manual con el que iniciarse con uno de los frameworks más potentes del momento en Javascript. El texto está creado para ser leído en el navegador, donde los fragmen- tos de código pueden ejecutarse directamente en la consola de Javas- cript. Al realizar la adaptación a un formato offline, como el PDF, esta posi- bilidad desaparece. Para evitar que el lector se quede sin el feedback proporcionado por la consola de Javascript, cada fragmento de código ejecutable en la web se acompaña de una captura de pantalla de la sa- lida por consola. De esta manera, se consigue una experiencia similar a la de leer el tutorial en la web. Cuando el código manipula el DOM -por ejemplo, cuando se trata de vistas-, el texto incluye capturas de la página web antes y después de la ejecución del código. En cuanto a los enlaces que aparecen en la página web, en la versión offline se han complementado con una nota al pie. El enlace sigue sien- do válido -clickable- en un formato como el PDF, pero se incluye la url como una nota a pie de página por si quieres imprimir el tutorial como referencia. Otro elemento que se queda fuera de la versión offline son los comen- tarios. A través de los comentarios puedes expresar dudas, quejas, agradecimientos, sugerencias, puntualizaciones, correcciones... Para mí son extremadamente importantes, pues me dicen qué hago bien, qué puedo mejorar... Me hacen saber que hay alguien ahí, al otro lado a quienes sirve este tutorial... Por tanto, te animo a visitar la página http://bit.ly/1erosPasosBackbone y hacer oir tu voz. Esta adaptación la he realizado utilizando LYX (http://www.lyx.org/), un programa libre y multiplataforma, que permite utilizar la potencia y flexibilidad del TEX y LATEX a través de un entorno gráfico que facilita su utilización. 3
  • 4. 1 PRIMEROS PASOS 1. Primeros pasos 1.1. Requisitos básicos En primer lugar, necesitas un navegador compatible con herramientas de desarrollador para ver qué está pasando en la consola. Cualquier navegador moderno servirá, aunque para elaborar este tutorial he uti- lizado Google Chrome. Para abrir la consola en Google Chrome, haz click en cualquier lugar de la página web y elige Inspect Element del menú contextual. Selec- ciona Console en la parte superior del panel con las herramientas de desarrollo. También puedes pulsar Crtl + Shift + J (o a través del menú de Chrome → Tools → Javascript console). 1.2. Enlazando Backbone Para poder praticar con Backbone, es recomendable que crees una pá- gina web en la que enlaces los diferentes componentes que requiere Backbone: jQuery, Underscore, json2 y backbone: <script type="text/javascript" src="jquery/jquery.min.js"></script> <script type="text/javascript" src="backbone/json2.min.js"></script> <script type="text/javascript" src="backbone/underscore.min.js"></script> <script type="text/javascript" src="backbone/backbone.min.js"></script> Aunque Backbone.js sólo tiene como dependencia indispensable a underscore, para realizar manipulación del DOM y poder gestionar persistencia RESTful completa, necesitas también json2 y jQuery. En realidad, json2 sólo es necesario si quieres dar soporte a navegado- res antiguos que no tengan soporte nativo para JSON, lo que cada día es menos frecuente. Para seguir los ejercicios de este tutorial, mantén siempre abierta la consola de Javascript de tu navegador. 4
  • 5. 2 MODELOS 2. Modelos 2.1. Nuestro primer modelo En primer lugar, creamos un modelo extendiendo la clase Model de Backbone. De esta forma creamos nuestra clase personalizada de Mo- delo. No parece un gran avance, pero en realidad Backbone nos ha pro- porcionado una serie de métodos para establecer (set) y obtener (get) propiedades de nuestro modelo, entre muchas otros métodos útiles. Utilizamos la convención habitual de que los nombres de las clases empiezan por mayúscula y las instancias, por minús- culas. El siguiente código muestra en la consola la creación de un modelo: Contacto = Backbone.Model.extend(); contacto = new Contacto(); console.log( contacto ); En la consola vemos que se ha creado algo... Utilizamos el método .toJSON() para convertir el objeto a formato JSON. 5
  • 6. 2.1 Nuestro primer modelo 2 MODELOS Utilizamos los métodos heredados de Model para establecer y obtener la propiedad nombre: Contacto = Backbone.Model.extend(); contacto = new Contacto(); contacto.set( ’nombre’, ’Anthony Machine’ ); console.log( contacto.toJSON() ); console.log( ’El nombre del contacto es ’ + contacto.get( ’nombre’ ) ); Durante la creación de nuestra instancia del modelo podemos pasar pares de clave:valor en un solo paso: Contacto = Backbone.Model.extend(); contacto = new Contacto({ ’nombre’: ’Anthony Machine’, ’telefono’: ’555-123-456’ }); console.log( contacto.toJSON() ); console.log( ’El telefono de ’ + contacto.get( ’nombre’ ) + ’ es el ’ + contacto.get( ’telefono’ ) ); También podemos especificar valores por defecto en la creación de nuestro modelo personalizado. Los valores por defecto se asignan si no se especifica un valor para la propiedad (como en el caso del telefono del segundo contacto): Contacto = Backbone.Model.extend({ defaults: { ’nombre’: ’John Doe’, 6
  • 7. 2.1 Nuestro primer modelo 2 MODELOS ’telefono’: ’900-123-456’ } }); contactoDesconocido = new Contacto(); contactoManoli = new Contacto({ ’nombre’: ’Manoli’ }); console.log( contactoDesconocido.toJSON() ); console.log( contactoManoli.toJSON() ); También podemos asignar funciones que actualicen una propiedad de forma programable. En este caso, lanzamos la función desde la consola para actualizar el nombre de un contacto creado con los valores por defecto. Contacto = Backbone.Model.extend({ defaults: { ’nombre’: ’John Doe’, ’telefono’: ’900-123-456’ }, actualizaNombre: function(){ var nuevoNombre = prompt( ’Introduce el nuevo nombre para el contacto: ’ ); this.set( {’nombre’: nuevoNombre} ); } }); contacto = new Contacto(); console.log( contacto.toJSON() ); contacto.actualizaNombre(); console.log( contacto.toJSON() ); En primer lugar, preguntamos al usuario el nombre que quiere asignar al contacto y después mostramos en la consola cómo hemos actualiza- do la propiedad nombre del contacto con el valor proporcionado. 7
  • 8. 2.1 Nuestro primer modelo 2 MODELOS Finalmente, vamos a añadir una función que vigile los cambios que se producen en la instancia del modelo creado y que nos avise al pro- ducirse un cambio. Este tipo de funciones se denominan listeners en inglés. El listener escucha cambios en el valor de nombre y lanza una función que registra el cambio en la consola. Contacto = Backbone.Model.extend({ defaults: { ’nombre’: ’John Doe’ }, actualizaNombre: function( nuevoNombre ){ this.set({ ’nombre’: nuevoNombre }); } }); // Creación del contacto con el nombre por defecto contacto = new Contacto(); console.log( contacto.toJSON() ); // Listener 8
  • 9. 3 COLECCIONES contacto.on( ’change:nombre’, function(){ console.log( ’Nombre del contacto modificado!’ ) } ); // Actualizamos el nombre contacto.actualizaNombre( ’Miguel Campoviejo’ ); console.log( contacto.toJSON() ); 3. Colecciones Las colecciones (Collection) son conjuntos ordenados de modelos. Como en el caso de los modelos, para crear una colección extendemos la clase Collection proporcionada por Backbone. Como una colección no es más que un conjunto de modelos, al crear la colección debemos especificar el tipo de modelo que contiene. También tenemos que especificar una url. Esto es necesario porque al realizar al- guna operación CRUD, Backbone intenta conectar mediante REST con la capa de persistencia, es decir, un servidor al otro lador de la URL que le proporcionamos; allí completará la acción de creación, actualización o eliminación de los datos de la colección. Si no proporcionamos esta URL, aparecerán errores en la consola de Javascript. Creamos la colección y la instanciamos, mostrando el resultado en la consola usando el método .toJSON(): Contacto = Backbone.Model.extend(); Contactos = Backbone.Collection.extend({ Model: Contacto, 9
  • 10. 3 COLECCIONES url: "#" }); contactos = new Contactos(); console.log( contactos.toJSON() ); El resultado es un array vacío. Una colección es un array de objetos, pero la colección no contiene todavía ningún elemento. Utilizamos el método .add() para añadir modelos a la colección. Contacto = Backbone.Model.extend(); Contactos = Backbone.Collection.extend({ Model: Contacto, url: "#" }); contactos = new Contactos(); contactos.add({ ’nombre’: ’Anthony Machine’ }); console.log( contactos.toJSON() ); 10
  • 11. 3 COLECCIONES Podemos añadir tantos objetos como queramos, repitiendo la acción .add(): Contacto = Backbone.Model.extend(); Contactos = Backbone.Collection.extend({ Model: Contacto, url: "#" }); contactos = new Contactos(); contactos.add({ ’nombre’: ’Anthony Machine’ }); contactos.add({ ’nombre’: ’Miguel Campoviejo’ }); console.log( contactos.toJSON() ); En vez de añadir los elementos de la colección uno a uno, podemos añadirlos durante la creación de la instancia de la colección: Contacto = Backbone.Model.extend(); Contactos = Backbone.Collection.extend({ model: Contacto, url: "#" }); contactos = new Contactos([ { ’nombre’: ’Anthony Machine’ }, 11
  • 12. 3 COLECCIONES { ’nombre’: ’Miguel Campoviejo’ } ]); console.log( contactos.toJSON() ); Podemos añadir un contacto a la colección en la posición que deseemos utilizando at. El siguiente código añade contacto1 en la posición 1 usando at, mientras que el contacto2 se añade al final (posición por defecto). Del mismo modo, usamos remove para eliminar un elemento de la co- lección: Contacto = Backbone.Model.extend(); contacto1 = new Contacto({ ’nombre’ : ’Mary Sun’ }); contacto2 = new Contacto({ ’nombre’ : ’Federico Mercurio’ }); Contactos = Backbone.Collection.extend({ model: Contacto, url: "#" }); contactos = new Contactos([ { ’nombre’: ’Anthony Machine’ }, { ’nombre’: ’Miguel Campoviejo’ } ]); contactos.add( contacto1, {at : 1} ); console.log( contactos.toJSON() ); 12
  • 13. 3 COLECCIONES contactos.add( contacto2 ); console.log( contactos.toJSON() ); // Eliminamos el primer elemento : primerElemento = contactos.at(0); contactos.remove( primerElemento ); console.log( contactos.toJSON() ); // Eliminamos un objeto determinado contactos.remove( contacto2 ); console.log( contactos.toJSON() ); Vamos a verlo con un poco más de detalle; 1. Creamos la colección con dos contactos, y añadimos contacto1 en la posición 1. Después, añadimos contacto2 sin especificar la posición, de manera que se añade al final de la colección: 13
  • 14. 3 COLECCIONES 2. Eliminamos el contacto situado en la posición 0 de la colección: 14
  • 15. 3 COLECCIONES 3. Finalmente, eliminamos el contacto2 (no hace falta especificar la posición; Backbone lo busca y lo elimina esté donde esté): 15
  • 16. 3 COLECCIONES Si queremos actuar sobre todos los elementos de la colección, un mé- todo como .each() nos resultará útil. En el siguiente ejemplo, recorre- mos los elementos de la colección y mostramos el nombre en la consola: Contacto = Backbone.Model.extend(); Contactos = Backbone.Collection.extend({ model : Contacto, url : ’#’ }); contactos = new Contactos([ { ’nombre’ : ’Anthony Machine’ }, { ’nombre’ : ’Miguel Campoviejo’ }, { ’nombre’ : ’Mary Sun’ } ]); contactos.each( function( cadaContacto ){ console.log( ’El nombre del contacto es ’ + cadaContacto.get( ’nombre’ ) ); 16
  • 17. 3 COLECCIONES } ); Finalmente, podemos establecer listeners que controlen si se producen cambios en la colección. En el siguiente ejemplo, vigilamos el evento add. Contacto = Backbone.Model.extend(); Contactos = Backbone.Collection.extend({ model: Contacto, url: ’#’ }); contactos = new Contactos(); //Listener contactos.on( ’add’, function(){ console.log( "Has cambiado la colección!" ); } ); var nuevoContacto = new Contacto({ ’nombre’ : ’Anthony Machine’ }); contactos.add( nuevoContacto ); 17
  • 18. 3 COLECCIONES Un solo listener puede reaccionar a diferentes eventos; para ello sólo tenemos que incluir los eventos separados por espacios. Modificamos el código anterior para reaccionar tanto a la adición como a la elimi- nación de elementos de la colección. Contacto = Backbone.Model.extend(); Contactos = Backbone.Collection.extend({ model: Contacto, url: ’#’ }); contactos = new Contactos(); //Listener contactos.on( ’add remove’, function(){ console.log( "Has cambiado la colección!" ); } ); var nuevoContacto = new Contacto({ ’nombre’ : ’Anthony Machine’ }); console.log( ’(Contacto añadido)’ ) contactos.add( nuevoContacto ); console.log( ’(Contacto eliminado)’ ); contactos.remove( nuevoContacto ); 18
  • 19. 4 VISTAS 4. Vistas El proceso para crear una vista (view en inglés) es extendiendo la clase View proporcionada por Backbone. Creamos la clase sin ningún pa- rámetro y examinamos qué es lo que nos proporciona una vista en Backbone. VistaPrincipal = Backbone.View.extend(); vistaPrincipal = new VistaPrincipal(); console.log( vistaPrincipal ); Al crear una vista obtenemos un objeto con diferentes propiedades. La propiedad cid es el identificador interno asignado por Backbone a la vista recién creada. Sin embargo, nos interesan más las propiedades el y $el (abreviatura de elemento). La propiedad el es el contenedor de la vista creada. Por defecto, Back- bone proporciona un div. Puedes pensar en una vista como una caja en la que mostrar el interfaz de la aplicación web. Todo lo que ten- gas que dibujar (render en inglés) en el interfaz -botones, formularios, listas, etc- se mostrará dentro del elemento el de la vista. Sin embargo, no debes pensar que la vista contiene el HTML de tu aplicación; contienen la lógica necesaria para presentar al usuario los datos contenidos en los modelos. La representación de los datos se realiza, en general, usando algún sistema de plantillas como los mi- crotemplates de Underscore.js, Mustache, etc... Después de recoger los 19
  • 20. 4 VISTAS datos y combinarlos con la plantilla, la vista almacena el resultado en el elemento el. El siguiente ejemplo muestra como, por defecto, el elemento el de una vista es un div vacío: VistaPrincipal = Backbone.View.extend(); vistaPrincipal = new VistaPrincipal(); console.log( vistaPrincipal.el ); Por otro lado, $el es la referencia vía jQuery al objeto contenedor de la vista (el div, en este caso). Así podemos hacer referencia al elemento y utilizar los métodos proporcionados por jQuery para manipularlo. Por ejemplo, $el.show(); sería equivalente a $(’div’).show(); (aunque probablemente deberías ser más preciso con el selector). Backbone proporciona un método render para dibujar la vista, aunque por defecto no hace nada. Backbone permite utilizar cualquier sistema para mostrar la interfaz en pantalla, así que debemos especificar cómo se muestra: utilizando concatenación de cadenas, mediante un motor de plantillas... Empezamos utilizamos el método .append() de jQuery para añadir un encabezado en la vista: VistaPrincipal = Backbone.View.extend({ render: function(){ this.$el.append( ’<h1>Hola Backbone!</h1>’ ); return this; } }); 20
  • 21. 4 VISTAS vistaApp = new VistaPrincipal(); vistaApp.render(); console.log( vistaApp.el ); Al llamar a la función render() de forma manual, hemos forzado a dibujar el encabezado en el div de la vista. Sin embargo, como he- mos utilizado .append(), si lanzamos .render() múltiples veces, el contenido se añade también múltiples veces... VistaPrincipal = Backbone.View.extend({ render: function(){ this.$el.append( ’<h1>Hola Backbone!</h1>’ ); return this; } }); vistaApp = new VistaPrincipal(); vistaApp.render(); vistaApp.render(); vistaApp.render(); console.log( vistaApp.el ); 21
  • 22. 4 VISTAS Si esto no es lo que queremos, podemos utilizar .html() para estable- cer el contenido de la vista, por ejemplo. VistaPrincipal = Backbone.View.extend({ render: function(){ this.$el.html( ’<h1>Hola Backbone!</h1>’ ); return this; } }); vistaApp = new VistaPrincipal(); vistaApp.render(); vistaApp.render(); vistaApp.render(); console.log( vistaApp.el ); Si queremos que la vista se dibuje automáticamente, podemos utilizar el método initialize. initialize se ejecuta al crear la instancia de la vista. VistaPrincipal = Backbone.View.extend({ 22
  • 23. 4 VISTAS initialize: function(){ this.render(); }, render: function(){ this.$el.html( ’<h1>Hola Backbone!</h1>’ ); return this; } }); vistaApp = new VistaPrincipal(); console.log( vistaApp.el ); Hasta ahora, toda la acción ha tenido lugar en la consola de Javascript. El siguiente paso será conseguir mostrar la aplicación web en el nave- gador. Backbone asigna por defecto al elemento el un div si no se ha especificado tagName, className o id. En el siguiente código asignamos una etiqueta H1 mediante tagName al elemento el. Durante la inicialización de la vista, podemos establecer variables, etc. En nuestro caso, preguntamos al usuario por su nom- bre. Después, llamamos a la función render, que se encarga de dibu- jar la vista. Finalmente, utilizamos jQuery para añadir la vista al DOM, dentro de un div con id=vista01. Este elemento div id="vista01" podría ser cualquier elemento previo del DOM, como body, por ejemplo. Una vez la vista se añade al DOM, aparece en pantalla. VistaPrincipal = Backbone.View.extend({ tagName : ’h1’, initialize: function(){ var nombre = prompt ( ’Como te llamas? n (Por defecto = Backbone)’ ); 23
  • 24. 4 VISTAS nombre = nombre || ’Backbone’; this.render( nombre ); }, render: function( nombre ){ this.$el.text( ’Hola ’ + nombre +’!’ ); $( ’#vista01’ ).html( this.el ); return this; } }); vistaApp = new VistaPrincipal(); Entre el fragmento de código y el siguiente párrafo tenemos div id="vista01" (vacío): Cuando ejecutamos el código, se pregunta al usuario: Se genera la vista y se añade al DOM, justo después del fragmento de código: 24
  • 25. 5 PLANTILLAS En Backbone, las vistas incorporan también la funcionalidad asociada a los elementos de la interfaz gráfica de la aplicación web. Es decir, que además de la "V" (de Vista), del modelo MVC, también contienen los controladores (la "C", del modelo MVC). Al pulsar un botón, añadir un elemento o modificar algún dato en la aplicación, establecemos listeners para los eventos que se disparen, y así hacer que la aplicación actualice aquello que sea necesario. 5. Plantillas Antes de entrar en el tema de la funcionalidad encapsulada en las vis- tas, creo que es conveniente comentar cómo mostrar en las vistas los datos contenidos en los modelos. Backbone nos permite mostrar los datos contenidos en los modelos co- mo queramos; no nos obliga a hacerlo de una forma determinada. El método más básico y directo, es simplemente obteniendo los datos des- de el modelo mediante un modelo.get( ’nombre’ ) e insertándolo en el DOM. Aunque podemos concaternar los valores obtenidos de los modelos con etiquetas HTML y añadirlas directamente al DOM del documento, esta solución no es la más recomendable. Underscore.js proporciona un sistema de microplantillas que podemos utilizar por defecto en nuestras aplicaciones, ya que underscore.js es una dependencia de Backbone.js. La función template permite insertar la información contenida en un modelo, convirtiéndola a formato JSON, en un documento HTML. Para ello, especificamos el nombre de la "clave" entre < %= ... %>, como se muestra en el siguiente ejemplo: 25
  • 26. 5 PLANTILLAS plantilla = _.template( ’Hola < %= nombre %>. Me encanta tu tema < %= cancion %>!’ ); console.log( plantilla( {nombre: ’Anthony Machine’, cancion: ’Little black angels’} ) ); _.template(...) analiza el código que le pasamos en busca de las etiquetas < %= ... %> que indican dónde debe insertar los valores del modelo. Si coinciden el nombre de la etiqueta y la clave del modelo, inserta el valor correspondiente. Si no lo encuentra, se muestra un error en la consola. Finalmente, devuelve el código de la plantilla con los valores del modelo ya insertados. Sin embargo, colocar trozos de código HTML junto con las etiquetas del motor de plantillas proporcionado por Underscore.js en el código javascript de la aplicación enturbia el código, haciéndolo menos legible y claro. La solución pasa por utilizar la otra dependencia de Backbone, jQuery y su capacidad para seleccionar y extraer elementos del DOM del do- cumento HTML. En el siguiente ejemplo selecciono el contenido del menú de navegación con el contenido de este documento y lo muestro a través de la consola: var $menu = $(’#menu ul’).html() console.log( $menu ); 26
  • 27. 5 PLANTILLAS Como ves, sólo necesitas saber qué quieres seleccionar. Puedes utilizar el id, el nombre de la class o la etiqueta (entre otras muchas opcio- nes). La idea será colocar el HTML de la plantilla que quieres utilizar para tus datos de la aplicación en el propio documento HTML. Para evitar que aparezca en el navegador, lo encerramos entre etiquetas script. Aunque el navegador no lo muestre, intentará interpretarlo como si fuera javascript (por defecto). Pero tampoco es éso lo que queremos, así que especificamos type="text/template; el navegador no sabrá como interpretarlo y por tanto, lo ignorará. En realidad podemos clasificarlo como text/LoQueSea, pero por convención se utiliza el tipo template (plantilla, en inglés). Finalmente, para poder acceder al fragmento de código que contiene la plantilla con facilidad, lo designamos con un id único. El fragmento de código a continuación muestra cómo podría ser una plantilla para mostrar la información del siguiente ejemplo: <script type="text/template" id="plantilla-vistapersonaje"> <h1> < %= nombre %> </h1> <p> <a href="< %= wikipediaurl %>" class="mas-info"> Información en la wikipedia sobre < %= nombre %> </a> </p> </script> 27
  • 28. 5 PLANTILLAS Ahora, el código de la vista para el contacto será: Personaje = Backbone.Model.extend(); VistaPersonaje = Backbone.View.extend({ el: ’#vista02’, plantilla: _.template( $(’#plantilla-vistapersonaje’).html() ), initialize: function( modelo ){ this.$el.html( this.plantilla ( modelo.toJSON() )); }, render: function(){ $(’#vista02’).html( this.$el ); return this; } }); personaje = new Personaje({ ’nombre’: ’Antonio Machín’, ’url’: ’http://es . wikipedia . org/wiki/Antonio_Mach %C3%ADn’ }) vistaPersonaje = new VistaPersonaje( personaje ); Antes de ejecutar el código: Y después, cuando hemos generado la vista a partir de la información contenida en el modelo: De esta forma conseguimos separar el código HTML de la plantilla de la funcionalidad relacionada con la vista. No hace falta preocuparse 28
  • 29. 5 PLANTILLAS sobre cómo se van a mostrar los datos de la aplicación, de los estilos o etiquetas en los que se encuentran. Sólo tienes que saber que en la plantilla habrá un hueco con la clave del dato que se insertará en esa posición (y dejar el diseño a un especialista, por ejemplo). Si se modifica la plantilla... <script type="text/template" id="plantilla-vistapersonaje2"> <ul> <li> <a href="< %= url %>"> < %= nombre %> </a></li> <li> <a href="< %= url %>"> < %= nombre %> </a></li> <li> <a href="< %= url %>"> < %= nombre %> </a></li> <li> <a href="< %= url %>"> < %= nombre %> </a></li> </ul> </script> ...el código no cambia: Personaje = Backbone.Model.extend(); VistaPersonaje = Backbone.View.extend({ el: ’#vista02’, plantilla: _.template( $(’#plantilla-vistapersonaje2’).html() ), initialize: function( modelo ){ this.$el.html( this.plantilla ( modelo.toJSON() )); }, render: function(){ $(’#vista02’).html( this.$el ); return this; } }); personaje = new Personaje({ ’nombre’: ’Antonio Machín’, ’url’: ’http://es . wikipedia . org/wiki/Antonio_Mach %C3%ADn’ }) vistaPersonaje = new VistaPersonaje( personaje ); sin embargo, la salida sí que es diferente, pues la plantilla que hemos utilizado es diferente: 29
  • 30. 6 CONTROLADORES Y EVENTOS Hasta ahora, sabemos que la vista tiene una propiedad el, que contie- ne una referencia a un elemento del DOM del documento. Mediante la función _.template(...) de Underscore.js, extraemos el HTML de la plantilla y lo combinamos con los datos almacenados en el modelo. Y mediante render(...) lo dibujamos en el documento HTML. Sin embargo, antes de empezar este apartado sobre plantillas decía que las vistas también contienen funcionalidad asociada a los elementos visuales de la aplicación. Vamos con esta funcionalidad... 6. Controladores y Eventos En el modelo MVC una aplicación se encuentra separada en tres tipos de componentes. En Backbone.js los controladores se incluyen en las vistas, con lo que no podríamos hablar estrictamente de un modelo MVC . En realidad, en las primeras versiones de Backbone.js sí que había una clase controller, solo que en la versión 0.5.0 se renombró a router. Más adelante hablo de los routers. El controlador no es más que una función que se encarga de reaccionar a las acciones que realiza el usuario sobre los componentes visuales de la aplicación y actualiza los datos del modelo en consecuencia. Para poder reaccionar a las acciones del usuario necesitamos un siste- ma que gestione los eventos que éste produce (hacer click en un botón, enviar un formulario, etc). 30
  • 31. 7 EVENTOS Y VISTAS Backbone.js proporciona un sistema de gestión de eventos que permite asociar listeners a los selectores incluidos en el elemento el (o directa- mente a el si no se proporciona ningún selector). Los eventos se recogen en forma de hash de la forma "evento selector" : "callback". El siguiente ejemplo muestra lo que podría ser un hash de eventos para una aplicación de agenda de contactos: events : { ’dblclick label’ : ’muestraEditaContacto’, ’blur .editar’ : ’cierraEditaContacto’, ’click .elimina’ : ’eliminaContacto’ } muestraEditaContacto, cierraEditaContacto y eliminaContacto son funciones que deben estar definidas en el mismo ámbito de la vista. En realidad, Backbone proporciona un sistema de eventos muy po- tente, que va más allá de las vistas y que permite gestionar eventos de modelos, colecciones, routers, el historial y de los objetos y eventos propios que definas para tu aplicación. Antes de centrarme en el uso de eventos relacionados con las vistas, comentaré brevemente los Eventos en Backbone.js. Backbone.Events puede utilizarse para asociar eventos personaliza- dos a cualquier objeto que definamos. 7. Eventos y vistas Una de las ventajas de Backbone.js es que los diferentes componentes que integran la aplicación -modelos, vistas y el DOM- se comunican 31
  • 32. 7 EVENTOS Y VISTAS entre sí. Esta comunicación es bidireccional, tanto si se actualiza la información contenida en el modelo como si se genera un evento en la vista que afecta al modelo. En Backbone.js la Vista hace de concentrador de todo lo que sucede en nuestra aplicación. Ya hemos visto antes que un cambio en el mo- delo permitía, a través de un listener, reaccionar ante un cambio en el modelo. En general, cuando cambia la información contenida en los modelos de nuestra aplicación querremos actualizar la vista, mostran- do la información actualizada. Del mismo modo, cuando se produce un evento en la vista, actualiza- mos el modelo o la colección correspondiente. 32
  • 33. 8 SINGLE PAGE APPLICATIONS (APLICACIONES DE UNA SOLA PÁGINA) 8. Single Page Applications (aplicaciones de una sola página) Hasta ahora hemos estado practicando directamente en la línea de co- mando, creando modelos, colecciones y vistas. También hemos visto que una de las ventajas de Backbone es que todos los elementos se comunican entre sí, a través de eventos asociados a los diferentes mo- delos o vistas de nuestra aplicación web. De hecho, Backbone.js pro- porciona toda la infraestructura necesaria para crear lo que se denomi- nan single page applications, es decir, una aplicación web de una sola página. Esta página contiene el HTML, las CSS y el código javascript necesario para interaccionar con los datos obtenidos desde el servidor y mostrarlos a través de las vistas. Imagina una aplicación de agenda como la que he dibujado más abajo. En la vista principal se muestra la lista completa de contactos (sin filtrar). Junto a cada uno de los contactos, tenemos un botón para editar la información del contacto. Al pulsarlo, se muestra una nueva página con la información del contacto. 33
  • 34. 8 SINGLE PAGE APPLICATIONS (APLICACIONES DE UNA SOLA PÁGINA) En una aplicación tradicional, el cliente solicita la página web al ser- vidor, donde se procesa la petición. Inicialmente, se devuelve una lista de todos los contactos. Cuando el cliente pulsa sobre un contacto para editar los datos relacionados, se realiza la petición al servidor, que ob- tiene los datos relativos al contacto seleccionado, se combinan con la plantilla adecuada y se devuelven al cliente. Como todo el procesamien- to de los datos se realiza en el servidor, cada vez que hay que tratar con los datos, hay que contactar de nuevo con el servidor. 34
  • 35. 8 SINGLE PAGE APPLICATIONS (APLICACIONES DE UNA SOLA PÁGINA) En una aplicación con Backbone.js, los datos se procesan en el cliente (el navegador): al solicitar la página, el servidor devuelve el documen- to HTML que contiene todas las plantillas que necesitan las vistas de la aplicación junto con los datos. Cuando el cliente interacciona con la aplicación, no es necesario volver a contactar con el servidor, sino simplemente cambiar la vista, filtrando los datos adecuados: 35
  • 36. 8 SINGLE PAGE APPLICATIONS (APLICACIONES DE UNA SOLA PÁGINA) El problema es que todas las vistas de la aplicación comparten la URL, por lo que hay que proporcionar algún método para que el usuario pueda acceder a una determinada funcionalidad de la aplicación web (por ejemplo, a la visualización de un contacto determinado). Usando REST esta URL será de la forma http://agenda.me/contacto/1, por ejemplo. El problema es que esta URL no existe. Sin embargo, podemos crear un enlace similar mediante el uso de una hash url; utilizando el signo de la almohadilla # indicamos al navegador que el enlace apunta a un fragmento de la página web, por lo que conseguimos URLs váli- das como http://agenda.me/#contacto/1. Otro problema relaciona- do con estas aplicaciones de una sóla página es que todos los enlaces con hash urls no eran indexados por los buscadores. Pero desde hace un tiempo los buscadores aceptan una especificación que permite inde- xar todas estas URLs virtuales, por llamarlas de algún modo. Si quieres que los buscadores indexen los enlaces de tu aplicación web, utiliza la combinación #!. Puedes completar información al respecto échandole un vistazo a https://developers.google.com/webmasters/ajax-crawling/ . 36
  • 37. 9 ROUTERS 9. Routers Para solucionar el problema de las URLs relacionados con la single page applications Backbone.js proporciona los router. Hasta la llegada de la API History había que utilizar las hash urls (p.ej. agenda.me/#edit) para proporcionar al usuario enlaces que pudieran ser enlazables, bookmarkables, etc. El problema es que para que un navegador pase de la URL agenda.me/contacto/1 a agenda.me/contacto/2, se requiere que se haga una petición al ser- vidor y que este devuelva el contenido de la URL agenda.me/contacto/2. Este proceso, llamado navegación actualiza la historia del navegador. Así podemos pulsar los botones atrás/adelante del navegador para avanzar/retroceder por el historial de navegación. Con las urls fragmentadas, cuando el navegador pasa de agenda.me/#contacto/1 a agenda.me/#contacto/2 no se produce un cambio de página, sino un cambio en la parte que se visualiza de una misma página. No hay llamada al servidor y por tanto, no hay actualización de la historia de navegación. Con la llegada de este API, las aplicaciones web pueden manipular la historia del navegador, de manera que un cambio de una URL a otra -aunque sea una hash url- sí se interpreta como una navegación entre páginas y, por tanto, queda registrado en la historia del navegador. De esta forma las urls fragmentadas pasan a comportarse como urls de verdad y pueden utilizarse para enlazar, para añadir a favoritos, para compartir por mail, etc. Backbone.js proporciona la clase Backbone.router para gestionar es- tos cambios de URLs en las aplicaciones del lado del cliente y asociarlas a acciones y eventos. Además, para los navegadores que no soportan el API de la historia de navegación el router se degrada de forma trans- parente a la versión hash de la URL. La creación de un router se realiza mediante la extensión de Backbone.Router, como es habitual en Backbone.js. Dentro de nuestra instancia de router definimos el conjunto de urls internas -llamadas rutas- de la aplicación y las funciones a las que 37
  • 38. 9 ROUTERS deben llamar cuando se navegue a una url determinada. En estas URLs podemos incluir una o varias partes variables, que será interpretadas como parámetros: var Agenda = Backbone.Router.extend({ routes : { "ayuda" : "ayuda", "busca/:consulta" : "busca" }, ayuda: function(){ ... }, busca: function( consulta ){ ... } }); En el ejemplo anterior he definido dos rutas; la primera corresponde a miagenda.#ayuda corresponde a una ruta básica. La aplicación mapea la ruta agenda.me/#ayuda con la función ayuda. En el segundo caso hemos definido la ruta añadiendo :consulta; de esta manera indicamos que este fragmento de la URL es un paráme- tro y que debe ser asignado a la variable consulta, que pasamos a la función correspondiente. Es decir, agenda.me/#busca/antonio resul- ta en busca( "antonio" ); en la aplicación. Con estos dos tipos de rutas tienes las opciones más comunes para de- finir rutas. De todas formas, consulta la documentación en Backbone.js/#Router para descubrir otras formas de pasar parámetros desde las URLs a las funciones de tu aplicación. Una vez creadas y configuradas las rutas de la aplicación web, indica- mos a Backbone.js que monitorice los eventos de cambio de hash urls y ¡listo! Backbone.history.start(); 38
  • 39. 9 ROUTERS Podemos pasar opciones a start, por ejemplo, si queremos usar pushState o si la página web no es servida directamente desde la raíz del do- minio... Como siempre, es recomendable que mires la documentación oficial completar información sobre History. Una vez arrancada la monitorización de cambios en la url, Backbone.js se encarga de actualizar el historial del navegador de forma automática. Si desde la aplicación web queremos forzar la navegación de un punto a otro de la aplicación, utilizamos el método navigate de router: ... this.navigate( "pagina/" + numPagina ); ... Si la url a la que estamos navegando forma parte de las rutas definidas, es probable que queramos que se dispare la función asociada definida en la ruta; para ello, pasamos {trigger:true} a navigate: routes: { "ayuda/faq" : "faqMsg" }, faqMsg: function(){ alert( ’Gracias por consultar las preguntas frecuentes’ ); } ... agenda.navigate( "ayuda/faq", {trigger: true} ); En resumen, definimos unas rutas en Backbone.Router, que mapea- mos a funciones relacionadas con las acciones que lleva a cabo la apli- cación. Con Backbone.history.start() la aplicación reacciona au- tomáticamente a los cambios de url, actualizando el historial de nave- gación. Si desde la aplicación queremos simular una de estas navega- ciones, usamos el método navigate. 39
  • 40. 10 COMUNICACIÓN CON EL SERVIDOR 10. Comunicación con el servidor Una de las ventajas de Backbone es que reduce las peticiones necesa- rias al servidor. En su lugar, Backbone utiliza las vistas para mostrar únicamente la información necesaria en la vista actual. Los datos de una aplicación web se almacenan en lo que se llama la ca- pa de persistencia, que normalmente es una base de datos. Esta base de datos se encuentra en el servidor, por lo que antes o después, desde la aplicación web hay que establecer comunicación con el servidor para actualizar datos. Backbone proporciona toda la infraestructura nece- saria para realizar la comunicación entre el navegador y el servidor de forma sencilla. Primero comentaré lo que pasa en general, usando únicamente REST. Después, veremos cómo Backbone ejecuta estos comandos a través del módulo Backbone.sync. sync es un mecanismo de bajo nivel, dentro del framework; en el día a día del desarrollo de la aplicación web uti- lizaremos otros métodos propios de modelos, colecciones y vistas, que son los que internamente utilizan sync y que comentaré al final de esta sección. Siguiendo con el ejemplo de la agenda, durante la primera conexión Backbone descarga la información de todos los contactos. Después, si el usuario consulta el contacto 1, muestra una vista de detalle con los datos filtrados para ése único usuario, sin necesidad de ponerse en contacto con el servidor de nuevo. A continuación vamos a entrar en detalle sobre cómo se comunica Backbone para obtener y actualizar la información. En la documentación oficial de Backbone dice que proporciona un in- terfaz REST completo a través de JSON (traducción libre de : [...] and connects it all to your existing API over a RESTful JSON interface). ¿Qué quiere decir ésto?. En la página en castellano de la Wikipedia encon- tramos la definición del significado de REST, de donde destaca: protocolo cliente/servidor sin estado: cada mensaje intercambia- do contiene toda la información necesaria para comprender la pe- tición. 40
  • 41. 10 COMUNICACIÓN CON EL SERVIDOR un conjunto de operaciones bien definidas: aunque incluye otras, en la práctica se utilizan sólo cuatro GET, POST, PUT, DELETE. sintaxis universal que permite identificar los recursos a través de su URI. Con estas ideas en mente, ahora volvemos a la aplicación de la agenda. Cuando el usuario accede a http://agenda.me/, es decir a la raíz de nuestra aplicación /, seguramente nos interese definir una route hacia /contactos/. Como no especificamos ningún id de contacto, si lo hemos programado así, nuestro servidor nos devolverá todos los contactos almacenados en la base de datos (mediante GET). Cuando el usuario introduce un nuevo contacto, enviamos la informa- ción al servidor mediante POST; el servidor se encarga de asignar el siguiente id libre en el equivalente en nuestra base de datos de la lista de /contactos. Si el usuario quiere acceder a un contacto concreto, como el que tiene el id=5, en este caso el identificador (URI del recurso) será: /contactos/5, de nuevo, usando GET. Mediante DELETE indicamos que vamos a borrar un contacto. Para ello debemos aportar el id del contacto a borrar; por ejemplo, como antes /contactos/5. Finalmente, usamos PUT para actualizar la información de un contacto (o crear uno nuevo). Como es lógico, aportamos el id del contacto que queremos acutalizar junto con la información actualizada. Backbone proporciona toda esta funcionalidad a través de Backbone.sync. sync toma tres parámetros: method, model y options (este último es opcional). El method es uno de los cuatro verbos de REST: GET, POST, PUT, DELETE. El segundo, model es el modelo a guardar en el servidor (o la colección que vamos a leer); Backbone interpreta qué tiene que hacer con los datos que le pasamos. Enlazando con el ejemplo anterior de la agenda, por defecto sync sería algo así: crear nuevo contacto : POST →/contactos 41
  • 42. 10 COMUNICACIÓN CON EL SERVIDOR leer (contacto o colección): GET → /contactos[/id] Si no se pro- porciona un id, es que queremos leer toda la colección. actualiza un contacto : PUT → /contactos/id borrar contacto : DELETE → /contactos/id Aunque Backbone.sync usa jQuery.ajax(), en realidad podemos sus- tituirlo por cualquier otro método que nos interese, en función de nues- tras necesidades. Un ejemplo de ello es la aplicación localtodos, que usa Backbone.localStorage. localStorage funciona como un plu- gin que usa el local storage (almacenamiento local) de HTML5 en vez de un servidor de bases de datos como capa de almacenamiento. Como he comentado al principio de esta sección, sync es un meca- nismo interno de Backbone; al desarrollar la aplicación web rara vez tendremos que llamar a sync de manera manual. De nuevo volvemos al ejemplo de la agenda. La primera vez que el usuario accede a la aplicación web crearemos una colección Contactos donde almacenar localmente la información obtenida del servidor. En la definición de la colección habremos incluido la propiedad url, que indica a Backbone dónde buscar y almacenar los datos contenidos en la colección. En la sección sobre Colecciones de este tutorial verás que utilicé url: ’#’ para evitar que Backbone se intentara comunicar con una url externa a la página. Ahora, suponiendo que ya tenemos el servidor configurado, nuestra colección incluiría: Contactos = Backbone.Collection.extend({ Model: Contacto, url: "/contactos/" }); Así la colección sabe dónde están almacenados los datos con los que se relaciona. Por tanto, en la función de inicialización de la colección podemos incluir: 42
  • 43. 10 COMUNICACIÓN CON EL SERVIDOR initialize: function(){ ... Contactos.fetch(); ... }; fetch obtiene los datos almacenados en la url de la colección en el servidor. En nuestro ejemplo de la agenda, obtendría todos los datos almacenados en la base de datos, pasándolos a una vista y mostrándo- los al usuario al visitar la página por primera vez. Incluso si llamamos a fetch en respuesta a algún evento que se produce en la aplicación, Backbone utiliza set para fusionar los datos locales con los obteni- dos del servidor de forma inteligente (a no ser que utilicemos {reset: true}). En las opciones de fetch podemos espeficificar funciones de callback para los eventos success y error de comunicación con el ser- vidor, actuando en consecuencia. A estas funciones Backbone les pasa automáticamente collection, response y options como argumen- tos. De hecho, fetch delega su ejecución a Backbone.sync de forma transparente (que a su vez utiliza jQuery.ajax() para realizar la co- municación con el servidor). fetch hace que se disparen los eventos add para cada modelo añadido y change para cada modelo cambiado. Así, si hemos especificado que ocurran cosas cuando cambien los modelos -por ejemplo, actualizar una vista- se ejecutará el código adecuado y todo funcionará sin que tengamos que preocuparnos por ello. fetch puede llamarse tanto para modelos como para colecciones. Si url no está definido para el modelo, utiliza url de la colección asocia- da. El único requerimiento para fetch es que el servidor devuelva un array de modelos en formato JSON. Volviendo la vista atrás un momento, tenemos que, lo que anteriormen- te habíamos comentado: leer (contacto o colección): GET → /contactos[/id] Si no se pro- porciona un id, es que queremos leer toda la colección. sería, mediante fetch: 43
  • 44. 10 COMUNICACIÓN CON EL SERVIDOR leer (contacto o colección): Contactos.fetch() (toda la colección) o Contacto.fetch() (para obtener un contacto en concreto). Al obtener el contacto no tenemos que especificar el id del modelo, ya que internamente Backbone sabe cuál es el id asociado a cada modelo de una colección, y no hace falta que lo especifiquemos nosotros. Para el resto de operaciones de creación, actualización o eliminación de modelos, tal y como hemos hecho con fetch utilizamos funciones específicas en vez de utilizar Backbone.sync a pelo. Backbone proporciona el método save para guardar un nuevo modelo en la base de datos o para actualizar uno existente. Contacto = Backbone.Model.extend(); var contacto = new Contacto({ nombre: ’Anthony Machine’, telefono: ’+34931234567’ }); contacto.save(); // Lo guarda en el servidor El siguiente trozo de código (adaptado de la página de Backbone) mues- tra una función sync modificada, que muestra un alert() en vez de comunicarse con el servidor. Al ejecutar el código por primera vez, save envía el modelo completo, ya que no existe inicialmente. En la segun- da llamada a save, especificamos sólo la parte de la información del contacto actualizada; Backbone te facilita el trabajo y no es necesario utilizar funciones diferentes para crear y actualizar ni es necesario es- cribir código para comprobar si un registro existe antes de modificarlo y cosas por el estilo... 44
  • 45. 10 COMUNICACIÓN CON EL SERVIDOR Backbone.sync = function(method, model) { alert(method + ": " + JSON.stringify(model)); model.id = 1; }; var contacto = new Backbone.Model({ nombre: "Anthony Machine", telefono: "+34931234567" }); // Primera llamada : crea el contacto contacto.save(); // Segunda llamada : actualiza el contacto ( que ya existe ) contacto.save({nombre: "Federico Mercurio"}); En la primera llamada: Y en la segunda: 45
  • 46. 10 COMUNICACIÓN CON EL SERVIDOR Para evitar enviar el modelo completo, podemos utilizar el parámetro {patch: true} que envía sólo los atributos modificados al servidor, optimizando las comunicaciones. Backbone utiliza isNew() para de- terminar si el modelo no existe -con lo que envía un HTTP POST, para crearlo- o si estamos haciendo una actualización, con lo que envía HTTP PUT. Finalmente, para eliminar un modelo del servidor, usaremos destroy(), lo que genera una petición HTTP DELETE. var contacto = new Backbone.Model({ nombre: "Justino Biever", telefono: "+34666234567" }); // Nos deshacemos del contacto contacto.destroy(); Obviamente, estas acciones están asociadas a los modelos que vamos a modificar. Si queremos llevar a cabo estas acciones (por ejemplo, eli- minar) para toda una colección, utilizamos el método de underscore.js _.each() para actuar sobre cada modelo de la colección. 46
  • 47. 10 COMUNICACIÓN CON EL SERVIDOR En esta sección he intentado clarificar un poco cómo gestiona Back- bone la comunicación del servidor. Backbone proporciona métodos es- pecíficos para modelos y/o colecciones que permiten realizar las ope- raciones básicas de manipulación de modelos y colecciones: fetch(), save(), destroy(), equivalentes a las operaciones CRUD (Crear, leer (read), actualizar (update) y destruir). Backbone es suficientemente inteligente como para distinguir cuándo se trata de crear o actua- lizar un modelo. Internamente estos métodos específicos delegan en Backbone.sync. Por defecto, sync usa la función .ajax() de jQuery (o de Zepto) para comunicarse con el servidor, aunque podemos cam- biarlo fácilmente. Además de proporcionar la infraestructura necesaria para realizar la comunicación con el servidor, Backbone también se encarga de dispa- rar eventos que permiten a otras partes de nuestra aplicación reaccio- nar ante los cambios que se produzcan en los modelos, independiente- mente de su origen. 47