Índice de contenidos.
1. Introducción.
En los pasados tutoriales de Backbone que os he ido posteando (Introducción a Backbone.js, Eventos y Modelos en Backbone.js ), hicimos una breve introducción sobre cuales eran las bases que componen este potentísimo framework y después extendimos contenido sobre cómo manejar los modelos de una aplicación y cómo hacer para que los distintos componentes de la misma «hablen» entre sí.
Pues bien, en este tutorial hablaremos sobre colecciones de modelos y vistas. Veremos cómo podemos gestionar los modelos a través de colecciones ordenadas y cómo podremos mostrar los datos de nuestra aplicación al usuario a través de las vistas.
2. Colecciones.
Para poder desarrollar nuestras aplicaciones, Backbone nos proporciona un potente componente llamado ‘Collection’, el cual nos permitirá gestionar de una manera muy sencilla colecciones ordenadas de modelos.
Al igual que los modelos, las colecciones lanzarán eventos en nuestra aplicación cada vez que éstas, o los modelos que contienen, cambien de estado. Después veremos la lista de eventos que las colecciones lanzarán por defecto a lo largo del ciclo de vida de las mismas.
Para poder crear una colección en nuestra aplicación, al igual que con los modelos, haremos uso de la función ‘extend’:
var Aula = Backbone.Collection.extend({});
model
Para indicar a la colección el tipo de modelo que va a gestionar se debe de sobrescribir el atributo ‘model’ indicando el tipo de modelo que va a gestionar:
var Alumno = Backbone.Model.extend({}); var Aula = Backbone.Collection.extend({ model: Alumno });
Creación de colecciones
Cuando creamos una colección tenemos varias opciones. Podemos crear la colección vacía y después añadir los modelos:
var Alumno = Backbone.Model.extend({}), alumno1 = new Alumno(), alumno2 = new Alumno(); var Aula = Backbone.Collection.extend({ model: Alumno }), aula = new Aula(); aula.add(alumno1); aula.add(alumno2);
O bien pasarle a la colección el conjunto de modelos que va a gestionar a través de aun array de modelos:
aula.add([alumno1, alumno2]);
models
Una vez creada la colección podemos recuperar los modelos que contiene la misma recuperando el atributo ‘models’, que nos devolverá un array con los modelos que contiene.
aula.models;
add
Una vez creada la colección usaremos la función ‘add’ para añadir modelos a la misma. Esta función puede recibir por parámetro el modelo en cuestión o un array con el conjunto de modelos a añadir a la colección.
remove
Para eliminar modelos de la colección, usaremos la función ‘remove’, la cual elimina el modelo o array de modelos que le pasemos por parámetro:
var 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, directamente usaremos la función ‘reset’.
set
Para actualizar modelos de la colección usaremos la función ‘set’. Esta función recibe por parámetro el modelo que vayamos a modificar. De igual manera que con la función ‘add’, puede recibir un array de modelos a actualizar. Si el modelo o modelos que la colección recibe no existe en su interior, la acción con ese modelo pasa automáticamente a ser una creación.
Debemos de 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, éstos, automáticamente serán eliminados de la colección.
var Alumno = Backbone.Model.extend({idAttribute: 'nombre'}), alumno1 = new Alumno({nombre: 'Isma'}), alumno2 = new Alumno({nombre: 'Alfonso'}); var Aula = Backbone.Collection.extend({ model: Alumno }), aula = new Aula(); aula.add(alumno1); aula.add(alumno2); console.log('Numero de alumnos en la colección: ' + aula.size()); alumno1.set({apellidos: 'Fernandez Molina'}); aula.set(alumno1); console.log('Numero de alumnos en la colección: ' + aula.size()); alumno1.set({apellidos: 'Lopez Garcia'}); alumno2.set({apellidos: 'Garcia Montes'}); aula.set([alumno1, alumno2]); console.log('Numero de alumnos en la colección: ' + aula.size());
get
Podemos recuperar modelos de la colección usando la función ‘get’, la cual recibe por parámetro del valor del ID del modelo que queremos buscar:
var Alumno = Backbone.Model.extend({ idAttribute: 'nombre' }), alumno1 = new Alumno({nombre: 'Isma'}), alumno2 = new Alumno({nombre: 'Alfonso'}); var Aula = Backbone.Collection.extend({ model: Alumno }), 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 de 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 meter y sacar modelo de la ‘pila’:
- Con la función ‘push(model)’ meteremos un modelo al final de la colección. Recibe por parámetro el modelo que se va a meter en la colección.
- Con la función ‘pop()’ sacamos y eliminamos el último modelo de la colección
unsifht/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.
Ordenación de las colecciones
Hemos hablado anteriormente de que la colecciones son conjuntos ordenados de modelos. Pues bien, Backbone, por defecto, no ordena nuestra colección a no ser que le digamos cómo hacerlo. Para ello debemos de indicarle, a través de la función ‘comparator’, el campo, o campos, del modelo por el cual se va a ordenar.
var Alumno = Backbone.Model.extend({ idAttribute: 'nombre' }), alumno1 = new Alumno({nombre: 'Alfonso', apellidos: 'Garcia Montes'}), alumno2 = new Alumno({nombre: 'Isma', apellidos: 'Fernandez Molina'}), alumno3 = new Alumno({nombre: 'Rodrigo', apellidos: 'Garcia Alvarez'}); var Aula = Backbone.Collection.extend({ model: Alumno }), aula = new Aula(); aula.comparator = function(model) { return model.get('apellidos'); }; aula.add(alumno1); aula.add(alumno2); aula.add(alumno3); console.log(aula.models);
Usaremos la función ‘sort’ para forzar a una colección a que se re-ordene. Aunque si hemos definido un ‘comparator’ no será necesario llamar a la función ‘sort’ ya que se ordenará de forma automática.
Búsqueda en colecciones
Para realizar búsqueda en colecciones, Backbone nos proporciona varias funciones.
var Alumno = Backbone.Model.extend({ idAttribute: 'idAlumo' }), alumno1 = new Alumno({idAlumno: 1, nombre: 'Alfonso', apellidos: 'Garcia Montes'}), alumno2 = new Alumno({idAlumno: 2, nombre: 'Ismael', apellidos: 'Fernandez Molina'}), alumno3 = new Alumno({idAlumno: 3, nombre: 'Ismael', apellidos: 'Garcia Alvarez'}); var Aula = Backbone.Collection.extend({ model: Alumno }), aula = new Aula(); aula.add([alumno1, alumno2, alumno3]);
La función where devuelve un array con todos los modelos que tengan un ‘match’ con la búsqueda en cuestión. Recibe por parámetro un hash con los parámetros de búsqueda:
aula.where({nombre: 'Ismael'}); aula.where({nombre: 'Ismael', apellidos: 'Fernandez Molina'});
La función findWhere realiza el mismo tipo de búsqueda que ‘where’, sólo que en vez de devolver todos los ‘match’ sólo devuelve el primer modelo que se encuentre el match’. Al igual que la función ‘where’, ésta recibe un hash con los parámetros de búsqueda.
Eventos
En el anterior tutorial, eventos y modelos en backbone, os comenté que los modelos de Backbone lanzan eventos, por defecto, cada vez que van cambiando a lo largo de su ciclo de vida. Pues bien, con las colecciones ocurre exactamente lo mismo. Cada vez que una colección o un modelo, que ésta contiene, cambia su estado, hay una serie de eventos que son lanzados al contexto de la aplicación:
- add: cuando un modelo es añadido a una colección.ó
- remove: cuando un modelo es eliminado de una colección.ó
- update: cuando añadimos o eliminamos modelos de una colección.ó
- reset: cuando ‘reseteamos’ el contenido de una colección.ó
- sort: cuando una colección se ha reordenado (probad a añadir un modelo a una colección que ya está ordenada).ó
- destroy: cuando la instancia de un modelo, que contiene la colección, es eliminada.ó
- request: cuando se lanza una petición hacia el servidor.ó
- sync: cuando la colección se ha sincronizado correctamente con el servidor.
- error: cuando falla una petición contra el servidor.
Como habéis podido ver, Backbone nos proporciona un montón de funciones que podemos usar para gestionar las colecciones de nuestra aplicación. Que duda cabe, que hay mchas más funciones. Aquí os he dejado las básicas y por las que podéis empezar a hacer vuestros pinitos.
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. Nos quedaría ver qué nos proporciona Backbone para mostrar al usuario toda esa información. Pues bien, para eso, están las vistas.
Para usarlas nos apoyaremos sobre librerías que nos permitan usar plantillas HTML (Underscore.js), para así, mostrar al usuario todos los datos que nuestra aplicación gestionará por detrás.
Para crear nuestra primera vista, al igual que con los demás componentes, usaremos la palabra reservada ‘extend’:
var AlumnosContainer = Backbone.View.extend({});
tagName
Con la sentencia anterior ya habremos creado nuestra primera vista. Por defecto, si no le indicamos nada, Backbone traducirá esa vista como un elemento HTML de tipo ‘div’ (<div></div>).
Para crear cualquier otro tipo de elemento HTML, debemos de sobrescribir el atributo ‘tagName’. En el siguiente ejemplo vamos a crear una lista (<ul></ul>):
var AlumnosContainer = Backbone.View.extend({ tagName: 'ul' });
className
Si sobrescribimos el atributo ‘className’, estaremos asignando asociando estilos (definidos en las hojas de estilos), al elemento que representa la vista en cuestión:
var AlumnosContainer = Backbone.View.extend({ className: 'container color-dark' });
El resultado HTML de la anterior vista sería la 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’ nos recupera un objeto jQuery que encapsulará el propio DOM.
var AlumnosContainer = Backbone.View.extend({ className: 'container color-dark' }); var alumnosContainer = new AlumnosContainer(); alumnosContainer.el; alumnosContainer.$el;
Cuando creamos una vista, el DOM de ésta se genera fuera del DOM del navegador. Por lo que 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 cual 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 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 cual 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.
var AlumnosContainer = Backbone.View.extend({ className: 'container color-dark', template: _.template('Hola <%=nombre%>', {nombre: 'Ismael'}) });
Si os dais cuenta, la función ‘template’ está recibiendo como primer parámetro la plantilla en cuestión y, como segundo, los parámetros que serán usados dentro de la plantilla, en este caso, un hash con el atributo ‘nombre’, cuyo valor será incrustado en la plantilla.
Underscore.js nos permite que definamos plantillas HTML identificadas por ‘id’ de manera que podamos hacer referencia a ellas mediante dicho ‘id’. Con esto nos ahorraríamos tener que estar escribiendo un string con todo el contenido de la misma. Por ahora prefiero que os quedéis con la clave de su uso y dejemos para el siguiente tutorial el profundizar sobre este tema.
render
Backbone llama a la función ‘render’ cada vez que la vista es renderizada. Sobrescribiremos esta función para renderizar la plantilla que hemos definido previamente. (Por convención se deberá de lanzar un ‘return this;’ al final de esta función:)
var 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 sustituir el DOM actual de la vista (por defecto es vacío), por el DOM que nos generará la plantilla creada previamente.
events
Como hemos aprendido en tutoriales previos, la comunicación entre los distintos componentes de nuestra aplicación será a través de 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 proceda.
La suscripción a los eventos se hace a través de la función ‘on’ y el lanzamiento con la función ‘trigger’. Pero que pasa si queremos que nuestra vista reaccione a determinados eventos que se produzcan dentro del DOM que ésta gestiona?.
Pues bien, para eso tenemos el atributo ‘events’. Este atributo contendrá un hash que definirá qué eventos, del DOM de la misma, serán los que escuche la vista (un click en un botón, onblur de un campo, 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:
var 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 lanzará la función ‘addAlumno’ cuando el usuario haga click sobre un elemento que contenga un class del tipo ‘icon-add’. Del mismo modo lanzará la función ‘removeAlumno’ cuando se haga click sobre un elemento que contenga un class del tipo ‘icon-remove’.
4. Conclusiones.
Tras este tutorial habéis podido comprobar cómo, de manera sencilla, somos capaces de gestionar colecciones de modelos, las cuales nos serán muy útiles a la hora de desarrollar nuestras aplicaciones.
Como hemos comentado en varias ocasiones, Backbone no nos da soporte para plantillas HTML. Debido a ello necesitaremos usar librerías de terceros. Como Backbone depende de Underscore.js nos aprovecharemos de la misma para usar su sistema de plantillas.
Cierto es que no he profundizado mucho sobre el sistema de plantillas de Underscore.js o el desarrollo de las vistas con Backbone. En realidad lo que quiero es que os vayan sonando los conceptos, funciones, atributos y demás elementos de cada componente ya que en el próximo tutorial desarrollaremos una aplicación completa con Backbone y será ahí donde consolidaremos cada uno de los conceptos que hemos ido viendo a lo largo de estos tutoriales.