2012-03-06 19 views
6

¿Existe alguna manera directa de insertar un nuevo artículo modelo en el medio de un backbone.js Collection y luego actualizar el View de la colección para incluir el nuevo elemento en la posición correcta?Insertar un elemento en una colección backbone.js

Estoy trabajando en un control para agregar/eliminar elementos de una lista. Cada elemento de la lista tiene su propio Model y View, y tengo View para toda la colección.

Cada vista de elemento tiene un botón Duplicate que clona el modelo del elemento y lo inserta en la colección en la posición de índice debajo del elemento al que se hizo clic.

Insertar el elemento en la colección fue sencillo, pero tengo problemas para averiguar cómo actualizar la vista de colección. He estado tratando de algo como esto:

ListView = Backbone.View.extend({ 
    el: '#list-rows', 
    initialize: function() { 
     _.bindAll(this); 
     this.collection = new Items(); 
     this.collection.bind('add', this.addItem); 
     this.render(); 
    }, 
    render: function() { 
     this.collection.each(this.addItems); 
     return this; 
    }, 
    addItem: function (item) { 
     var itemView = new ItemView({ model: item }), 
      rendered = itemView.render().el, 
      index = this.collection.indexOf(item), 
      rows = $('.item-row'); 

     if (rows.length > 1) { 
     $(rows[index - 1]).after(rendered); 
     } else { 
     this.$el.append(rendered); 
     } 
    } 
} 

Esta aplicación es una especie de trabajando, pero yo estoy errores extraños cuando agrego un nuevo elemento. Estoy seguro de que puedo resolverlos, pero ...

Hay una voz en mi cabeza que me dice que hay una mejor manera de hacerlo. Tener que averiguar manualmente dónde insertar un nuevo ItemView parece realmente hacky. ¿No debería la vista de colección saber ya cómo deshacerse de la colección?

¿Alguna sugerencia?

Respuesta

4

La manera habitual en que lo hago es dejar que ListView represente cada ItemView en su función render. Entonces sólo ato caso add a la función render, así:

ListView = Backbone.View.extend({ 
    el: '#list-rows' 
    , initialize: function() { 
    _.bindAll(this); 
    this.collection = new Items(); 
    this.collection.bind('add', this.render); 
    this.render(); 
    } 
    , render: function() { 
    this.$el.empty(); 
    var self = this; 
    this.collection.each(function(item) { 
     self.$el.append(new ItemView({ model: item }).render().el); 
    }); 
    return this; 
    } 
} 

Cada vez que se llama a this.collection.add(someModel, {at: index}), la vista se volverá a representar en consecuencia.

+0

Tuve que agregar una llamada a '(.item-row) .remove()' dentro del método 'render' para que funcione, pero es una buena solución. (De lo contrario, los elementos reintegrados se agregaron al final de los elementos renderizados existentes.) ¡Gracias por la ayuda! –

+0

Oh, puedes hacer 'this. $ El.empty()' para borrar el '$ el' antes de pasar por la colección :) – sntran

+0

Así es exactamente como estoy agregando elementos a la colección actualmente. Está colocando el nuevo artículo en el lugar correcto de la colección.Me di cuenta de que tengo un par de errores tipográficos en mi comentario anterior: llamo '$ ('. Item-row'). Remove()' para borrar los elementos que se crearon la última vez que se procesó la colección. Solo está restableciendo la vista. Ahora que lo pienso, hay un método de "reinicio" que puede hacer lo que quiero. Intentaré llamar y luego llamar a 'render'. –

9

No creo que re-renderizar toda la colección al agregar un nuevo elemento es la mejor solución. Es más lento que insertar el nuevo elemento en el lugar correcto, especialmente si la lista es larga.

Además, tenga en cuenta la situación siguiente. Cargue algunos elementos en sus colecciones y luego agregue n más elementos (supongamos que el usuario hace clic en el botón "cargar más"). Para hacer esto, debe llamar al método fetch() pasando add: true como una de las opciones. A medida que los datos se reciben desde el servidor, el evento 'agregar' se dispara n veces y terminaría re-renderizando su lista n veces.

En realidad estoy usando una variante del código en su pregunta, aquí está mi devolución de llamada para el evento 'añadir':

var view, prev, prev_index; 
view = new ItemView({ model: new_item }).render().el; 
prev_index = self.model.indexOf(new_item) - 1; 
prev = self.$el.find('li:eq(' + prev_index + ')'); 
if (prev.length > 0) { 
    prev.after(view); 
} else { 
    self.$el.prepend(view); 
} 

Así que, esencialmente, sólo estoy usando el selector de jQuery :eq() en vez de conseguir todos los elementos como lo haces, deberían usar menos memoria.

+0

Esto funciona muy bien, pero estoy mostrando números al lado de cada elemento en la lista, por lo que tengo que volver a hacer para mantener los números sincronizados. No puedo ver ninguna forma de evitar eso. Bummer ... Me gusta la simplicidad aquí. – byron

Cuestiones relacionadas