Colecciones y Vistas en Backbone.js
Por Ismael Fernández | 165 vistas | Lectura de 10 minutos
Por Ismael Fernández Molina
1. Introducción
En los tutoriales anteriores de Backbone que compartí (Introducción a Backbone.js, Eventos y Modelos en Backbone.js), dimos una breve introducción sobre las bases que conforman este potente framework y luego profundizamos en cómo manejar los modelos de una aplicación y cómo hacer que los distintos componentes se comuniquen entre sí.
En este tutorial hablaremos sobre colecciones de modelos y vistas. Aprenderemos cómo podemos gestionar los modelos a través de colecciones ordenadas y cómo mostrar los datos de nuestra aplicación al usuario mediante las vistas.
2. Colecciones
Para poder crear nuestras aplicaciones, Backbone nos ofrece un componente muy útil llamado 'Colección'
, que nos permite manejar de manera sencilla colecciones ordenadas de modelos.
Al igual que los modelos, las colecciones lanzan eventos en nuestra aplicación cada vez que cambian su estado o los modelos que contienen lo hacen. En la siguiente sección, veremos la lista de eventos que se generan por defecto a lo largo del ciclo de vida de las colecciones.
Para crear una colección en nuestra aplicación, al igual que con los modelos, debemos utilizar la función 'extend'
:
let Aula = Backbone.Collection.extend({});
model
Para que la colección pueda saber qué tipo de modelo va a manejar, se debe reescribir el atributo "model" y especificar el tipo de modelo que se va a manejar:
let Alumno = Backbone.Model.extend({});
let Aula = Backbone.Collection.extend({ model: Alumno });
Creación de colecciones
Cuando creamos una colección, existen varias opciones. Podemos crear una colección vacía y luego agregar los modelos:
let Alumno = Backbone.Model.extend({});
let alumno1 = new Alumno();
let alumno2 = new Alumno();
let Aula = Backbone.Collection.extend({ model: Alumno })
let aula = new Aula();
aula.add(alumno1);
aula.add(alumno2);
O también podemos pasarle a la colección el conjunto de modelos que va a gestionar a través de un array de modelos:
aula.add([alumno1, alumno2]);
models
Una vez creada la colección, podemos obtener los modelos que contiene accediendo al atributo "models", el cual nos devolverá un arreglo con los modelos que contiene la colección.
aula.models;
add:
Después de haber creado la colección, podemos utilizar la función "add" para añadir modelos a la misma. Podemos pasar un modelo o un array de modelos como parámetro a esta función.
remove:
Para eliminar modelos de la colección, utilizaremos la función "remove". Esta función eliminará el modelo o un array de modelos que se pasen como parámetro.
let aula = new Aula();
aula.add(alumno1);
aula.add(alumno2);
console.log('Numero de alumnos en la colección: ' + aula.size());
aula.remove(alumno1);
console.log('Numero de alumnos en la colección tras eliminar modelo: ' + aula.size());
aula.add(alumno1);
aula.remove([alumno1, alumno2]);
console.log('Numero de alumnos en la colección tras eliminar modelo: ' + aula.size());
Si lo que queremos es vaciar la colección, podemos utilizar la función 'reset'
.
set
Para actualizar los modelos de una colección, utilizamos la función 'set'
. Esta función recibe por parámetro el modelo que se va a modificar. Al igual que con la función 'add'
, puede recibir un array de modelos a actualizar. Si el modelo o modelos que se pasan por parámetro no existen en la colección, automáticamente se creará un nuevo modelo.
Sin embargo, debemos tener cuidado con esta función, ya que si la colección contiene modelos que no están presentes en los modelos que se pasan por parámetro, estos modelos serán eliminados de la colección.
let Alumno = Backbone.Model.extend({ idAttribute: 'nombre' });
let alumno1 = new Alumno({ nombre: 'Isma' });
let alumno2 = new Alumno({ nombre: 'Alfonso' });
let Aula = Backbone.Collection.extend({ model: Alumno });
let aula = new Aula();
aula.add(alumno1);
aula.add(alumno2);
console.log('Número de alumnos en la colección: ' + aula.size());
alumno1.set({ apellidos: 'Fernández Molina' });
aula.set(alumno1);
console.log('Numero de alumnos en la colección: ' + aula.size());
alumno1.set({ apellidos: 'López García' });
alumno2.set({ apellidos: 'García Montes' });
aula.set([alumno1, alumno2]);
console.log('Numero de alumnos en la colección: ' + aula.size());
get
Para recuperar modelos de la colección, podemos usar la función 'get'
, que recibe como parámetro el valor del ID del modelo que deseamos buscar:
let Alumno = Backbone.Model.extend({ idAttribute: 'nombre' });
let alumno1 = new Alumno({ nombre: 'Isma' });
let alumno2 = new Alumno({ nombre: 'Alfonso' });
let Aula = Backbone.Collection.extend({ model: Alumno });
let aula = new Aula();
aula.add(alumno1);
aula.add(alumno2);
aula.get('Alfonso');
aula.get('Rodrigo');
at
Cuando tenemos una colección ordenada de modelos es interesante poder recuperar modelos que se encuentren en alguna posición determinada. Para ello, tenemos la función 'at'
la cual recibe la posición en la cual se debe encontrar el modelo en cuestión.
push/pop
Podemos hacer uso de la colección como si de una pila se tratase. Backbone nos proporciona dos funciones que nos permiten agregar y sacar modelo de la 'pila'
:
Con la función 'push(model)'
agregamos un modelo al final de la colección. Recibe por parámetro el modelo que se va a agregar en la colección. Con la función 'pop()'
sacamos y eliminamos el último modelo de la colección.
unshift/shift
Existen otras dos funciones que hacen exactamente lo mismo que 'push'
y 'pop'
. La única diferencia es que estas funciones trabajan sobre la cabecera de la colección en vez de trabajar sobre la cola.
Ordenamiento de las colecciones
Anteriormente hemos mencionado que las colecciones son conjuntos ordenados de modelos. Sin embargo, Backbone no ordena automáticamente nuestras colecciones a menos que le indiquemos cómo hacerlo. Para ello, debemos utilizar la función 'comparator'
y especificar el campo o campos del modelo por los que se realizará el ordenamiento.
let Alumno = Backbone.Model.extend({ idAttribute: 'nombre' });
let alumno1 = new Alumno({ nombre: 'Alfonso', apellidos: 'García Montes' });
let alumno2 = new Alumno({ nombre: 'Isma', apellidos: 'Fernández Molina' });
let alumno3 = new Alumno({ nombre: 'Rodrigo', apellidos: 'García Alvarez' });
let Aula = Backbone.Collection.extend({ model: Alumno });
let aula = new Aula();
aula.comparator = model => model.get('apellidos')
aula.add(alumno1);
aula.add(alumno2);
aula.add(alumno3);
console.log(aula.models);
Utilizaremos la función 'ordenar'
para obligar a una colección a reordenarse. Aunque si hemos definido un 'comparador'
, no será necesario llamar a la función 'ordenar'
, ya que se ordenará automáticamente.
Búsqueda en colecciones
Para realizar búsquedas en colecciones, Backbone nos proporciona varias funciones.
let Alumno = Backbone.Model.extend({ idAttribute: 'idAlumo' });
let alumno1 = new Alumno({ idAlumno: 1, nombre: 'Alfonso', apellidos: 'García Montes' });
let alumno2 = new Alumno({ idAlumno: 2, nombre: 'Ismael', apellidos: 'Fernández Molina' });
let alumno3 = new Alumno({ idAlumno: 3, nombre: 'Ismael', apellidos: 'García Alvarez' });
let Aula = Backbone.Collection.extend({ model: Alumno });
let aula = new Aula();
aula.add([alumno1, alumno2, alumno3]);
La función 'where'
devuelve un arreglo con todos los modelos que coincidan con la búsqueda realizada. Recibe como parámetro un objeto con los criterios de búsqueda:
aula.where({ nombre: 'Ismael' });
aula.where({ nombre: 'Ismael', apellidos: 'Fernández Molina' });
La función findWhere
realiza la misma búsqueda que 'where'
, pero en lugar de devolver todos los resultados, sólo devuelve el primer modelo que encuentre el resultado. Al igual que la función 'where'
, recibe un hash con los parámetros de búsqueda.
Eventos
En el tutorial anterior sobre eventos y modelos en Backbone, se mencionó que los modelos de Backbone lanzan eventos por defecto cada vez que cambian a lo largo de su ciclo de vida. Lo mismo ocurre con las colecciones. Cada vez que una colección o un modelo que contiene la colección cambia su estado, se lanzan una serie de eventos en el contexto de la aplicación:
- add: cuando se agrega un modelo a una colección.
- remove: cuando se elimina un modelo de una colección.
- update: cuando se agregan o eliminan modelos de una colección.
- reset: cuando se reinicia el contenido de una colección.
- sort: cuando se reordena una colección (prueba a agregar un modelo a una colección que ya está ordenada).
- destroy: cuando se elimina la instancia de un modelo que contiene la colección.
- request: cuando se realiza una solicitud al servidor.
- sync: cuando la colección se ha sincronizado correctamente con el servidor.
- error: cuando falla una solicitud al servidor.
Como se puede ver, Backbone proporciona una gran cantidad de funciones que se pueden utilizar para gestionar las colecciones de una aplicación. Sin duda, hay muchas más funciones, pero estas son las básicas con las que se puede empezar.
3. Vistas
Hasta este punto, hemos aprendido cómo gestionar los modelos y colecciones de nuestra aplicación y cómo podemos comunicarnos con los diferentes componentes de nuestra aplicación a través de eventos. Ahora, vamos a ver lo que nos proporciona Backbone para mostrar al usuario toda esa información. Para ello, están las vistas.
Para utilizarlas, nos apoyaremos en librerías que nos permitan usar plantillas HTML (Underscore.js) para mostrar al usuario todos los datos que nuestra aplicación gestionará detrás de escena.
Para crear nuestra primera vista, al igual que con los demás componentes, usaremos la palabra reservada "extend":
let AlumnosContainer = Backbone.View.extend({});
tagName
Con la sentencia anterior, ya habremos creado nuestra primera vista. Por defecto, si no le indicamos nada, Backbone interpretará esa vista como un elemento HTML de tipo "div" (<div></div>)
.
Para crear cualquier otro tipo de elemento HTML, debemos sobrescribir el atributo "tagName". En el siguiente ejemplo, vamos a crear una lista (<ul></ul>)
:
let AlumnosContainer = Backbone.View.extend({
tagName: 'ul'
});
className
Si sobrescribimos el atributo "className", estaremos asignando estilos (definidos en las hojas de estilo) al elemento que representa la vista en cuestión:
let AlumnosContainer = Backbone.View.extend({
className: 'container color-dark'
});
El resultado HTML de la vista anterior sería el siguiente: <div class='container color-dark'></div>
el y $el
Toda vista contendrá las propiedades "el"
y "$el"
, mediante las cuales podemos hacer referencia al DOM de la propia vista. A través de "el"
, accederemos al DOM de la vista, mientras que con "$el"
recuperamos un objeto jQuery que encapsula el propio DOM.
let AlumnosContainer = Backbone.View.extend({
className: 'container color-dark'
});
let alumnosContainer = new AlumnosContainer();
alumnosContainer.el;
alumnosContainer.$el;
Cuando creamos una vista, el DOM de ésta se genera fuera del DOM del navegador. Por lo tanto, será necesario asociar al DOM del navegador las vistas que queramos renderizar en cada momento (esto lo veremos de forma más detallada en tutoriales futuros).
template
Con este atributo, indicamos a Backbone cuál será la plantilla HTML que usará la vista para mostrar los datos al usuario. Como hemos comentado varias veces, Backbone no da soporte a plantillas, por lo que usaremos librerías de terceros para usarlas. En este caso, usaremos Underscore.js (cuando programemos la aplicación completa, ya veremos en detalle cómo usarla).
Mediante la función "template" de Underscore (representado por "_"), indicaremos cuál es la plantilla a asociar a la vista. Como primer parámetro, recibe la plantilla en cuestión y como segundo, los datos que incrustaremos en la plantilla HTML.
let AlumnosContainer = Backbone.View.extend({
className: 'container color-dark',
template: _.template('Hola <%=nombre%>', { nombre: 'Ismael' })
});
Si te das cuenta, la función "template" recibe como primer parámetro la plantilla y, como segundo, los parámetros que serán utilizados dentro de la plantilla. En este caso, se pasa un hash con el atributo 'nombre'
, cuyo valor se insertará en la plantilla.
Underscore.js nos permite definir plantillas HTML identificadas por un 'id'
, para que podamos hacer referencia a ellas mediante ese 'id'
. Esto nos ahorra tener que escribir una cadena con todo el contenido de la plantilla. Por ahora, es mejor que se entienda su uso y dejemos para el siguiente tutorial profundizar en este tema.
render
Backbone llama a la función 'render'
cada vez que se renderiza la vista. Sobrescribiremos esta función para renderizar la plantilla que hemos definido previamente. (Por convención, al final de esta función deberemos incluir 'return this;'
)
let AlumnosContainer = Backbone.View.extend({
className: 'container color-dark',
template: _.template('Hola ', { nombre: 'Ismael' }),
render: function() {
this.$el.html(this.template());
return this;
}
});
En la función anterior, lo que se está haciendo es reemplazar el DOM actual de la vista (que por defecto está vacío) con el DOM que generará la plantilla creada previamente.
events
En cuanto a los eventos, como hemos aprendido en tutoriales anteriores, la comunicación entre los distintos componentes de nuestra aplicación se realiza mediante eventos. Cada uno de los componentes de la aplicación lanzará eventos que serán escuchados por otros, que cambiarán su estado según corresponda.
La suscripción a los eventos se realiza mediante la función 'on'
y el lanzamiento de los eventos se realiza mediante la función 'trigger'
. Pero ¿qué sucede si queremos que nuestra vista reaccione a ciertos eventos que se produzcan dentro del DOM que ésta gestiona?
Bueno, para eso tenemos el atributo 'events'
. Este atributo contendrá un hash que definirá qué eventos del DOM de la vista serán escuchados (por ejemplo, un clic en un botón, la pérdida de foco de un campo, la pulsación de una tecla, etc.). El hash contendrá la definición del evento y la función que se ejecutará cuando dicho evento se produzca:
let AlumnosContainer = Backbone.View.extend({
className: 'container color-dark',
template: _.template(templateHTML, { nombre: 'Ismael' }),
render: function() {
this.$el.html(this.template());
return this;
},
events: {
'click .icon-add': 'addAlumno',
'click .icon-remove': 'removeAlumno',
},
addAlumno: function() {
console.log('se va a añadir un alumno');
},
removeAlumno: function() {
console.log('se va a eliminar un alumno');
}
});
En este ejemplo, se ejecutará la función 'addAlumno'
cuando el usuario haga clic en un elemento que tenga una clase del tipo 'icon-add'
. De la misma manera, se ejecutará la función 'removeAlumno'
cuando se haga clic en un elemento que tenga una clase del tipo 'icon-remove'
.
4. Conclusiones
En este tutorial pudiste ver cómo es posible manejar colecciones de modelos de manera sencilla, lo que será muy útil para el desarrollo de tus aplicaciones.
Como hemos mencionado varias veces, Backbone no proporciona soporte para plantillas HTML, por lo que necesitaremos usar librerías de terceros. Como Backbone depende de Underscore.js, aprovecharemos su sistema de plantillas.
No profundizamos mucho en el sistema de plantillas de Underscore.js o en el desarrollo de vistas con Backbone. Lo importante es que vayas familiarizándote con los conceptos, funciones, atributos y otros elementos de cada componente, ya que en el próximo tutorial desarrollaremos una aplicación completa con Backbone y consolidaremos los conceptos que hemos visto en estos tutoriales.
Eventos Globales de Aplicaciones en Titanium utilizando Backbone.js
TitaniumPor Jason Kneen | 537 vistas | Lectura de 3 minutos
Facilitando el data-binding con Alloy: Parte 1
TitaniumPor Jason Kneen | 462 vistas | Lectura de 4 minutos
Facilitando el data-binding con Alloy: Parte 2
TitaniumPor Jason Kneen | 436 vistas | Lectura de 2 minutos