2012-09-05 10 views
10

Tengo una colección Backbone con una carga de modelos.Guarde varios modelos Backbone a la vez

Cada vez que se establece un atributo específico en un modelo y se guarda, una carga de cálculos se dispara y la UI vuelve a fragmentar.

Pero, quiero ser capaz de establecer atributos en varios modelos a la vez y solo guardar y volver a enviar una vez que estén todos configurados. Por supuesto, no quiero hacer varias solicitudes http para una operación y definitivamente no quiero tener que volver a enviar la interfaz diez veces.

Esperaba encontrar un método para guardar en Backbone.Collection que podría determinar qué modelos tieneChanged(), agréguelos como json y envíelos al back-end. La redención podría ser desencadenada por un evento en la colección. No tal suerte.

Esto parece ser un requisito muy común, así que me pregunto por qué Backbone no implementa. ¿Esto va en contra de una arquitectura RESTful, para guardar varias cosas en un único punto final? Si es así, ¿y qué? No hay forma de que sea práctico realizar 1000 solicitudes para persistir 1000 artículos pequeños.

Entonces, ¿es la única solución para aumentar Backbone.Collection con mi propio método de guardado que itera sobre todos sus modelos y construye el json para todos los que han cambiado y lo envía al back-end? o ¿alguien tiene una solución más ordenada (o me estoy perdiendo algo?)?

+3

Relacionados: http://stackoverflow.com/questions/11819662/why-does-backbone-not-have-a-save-put-post-method-for-its-collections-is-i/11819965#11819965 | http://stackoverflow.com/questions/11042292/extend-backbone-sync-to-batch-sync | http://stackoverflow.com/questions/511281/patterns-for-handling-batch-operations-in-rest-web-services | http://stackoverflow.com/questions/8529919/how-to-update-a-set-of-records-using-backbone-js-rails – fguillen

Respuesta

4

He terminado aumentando Backbone.Collection con un par de métodos para manejar esto.

saveChangeMethod crea un modelo ficticio que se pasará a Backbone.sync. Todas las necesidades del método de sincronización de la red troncal desde un modelo son su propiedad url y el método TOJSON, por lo que podemos simplificarlo fácilmente.

Internamente, el método toSON de un modelo solo devuelve una copia de sus atributos (para enviarlo al servidor), por lo que podemos utilizar un método TOJSON que simplemente devuelve la matriz de modelos. Backbone.sync lo corrige, lo que nos da solo los datos de los atributos.

En caso de éxito, saveChanged desactiva los eventos en la colección para ser manejados una vez. Han arrojado un código que lo hace disparar eventos específicos una vez por cada uno de los atributos que han cambiado en cualquiera de los modelos del lote.

Backbone.Collection.prototype.saveChanged = function() { 
    var me = this, 
     changed = me.getChanged(), 
     dummy = { 
      url: this.url, 
      toJSON: function() { 
       return changed.models; 
      } 
     }, 
     options = { 
      success: function (model, resp, xhr) { 
       for (var i = 0; i < changed.models.length; i++) { 
        changed.models[i].chnageSilently(); 
       } 
       for (var attr in changed.attributes) { 
        me.trigger("batchchange:" + attr); 
       } 
       me.trigger("batchsync", changed); 
      } 
     }; 
    return Backbone.sync("update", dummy, options); 
} 

Solo necesitamos el método getChanged() en una colección. Esto devuelve un objeto con 2 propiedades, una matriz de los modelos modificados y un decaído objeto que atributos han cambiado:

Backbone.Collection.prototype.getChanged = function() { 
    var models = [], 
     changedAttributes = {}; 
    for (var i = 0; i < this.models.length; i++) { 
     if (this.models[i].hasChanged()) { 
      _.extend(changedAttributes, this.models[i].changedAttributes()); 
      models.push(this.models[i]); 
     } 
    } 
    return models.length ? {models: models, attributes: changedAttributes} : null; 
} 

Aunque esto es ligero abuso del uso pretendido de 'modelo cambiada' backbones paradigma, el punto entero de dosificación es que no queremos que pase nada (es decir, cualquier evento que se desactive) cuando se cambia un modelo.

Por lo tanto, tenemos que pasar {silent: true} al método set() del modelo, por lo que tiene sentido usar el comando hasChanged() de la red troncal para marcar los modelos que esperan ser guardados. Por supuesto, esto sería problemático si cambiaras los modelos silenciosamente para otros fines: collection.saveChanged() los guardaría también, por lo que vale la pena considerar establecer un indicador alternativo.

En cualquier caso, si lo hacemos de esta forma, al asegurarnos de que la red troncal cree que los modelos no han cambiado (sin activar sus eventos de cambio), entonces debemos manipular manualmente el modelo como si no había sido cambiado. El saveChanged() itera método sobre nuestros modelos modificados y llama a este método changeSilently() en el modelo, que es básicamente el método model.change de Backbone() sin los factores desencadenantes:

Backbone.Model.prototype.changeSilently = function() { 
    var options = {}, 
    changing = this._changing; 
    this._changing = true; 
    for (var attr in this._silent) this._pending[attr] = true; 
    this._silent = {}; 
    if (changing) return this; 

    while (!_.isEmpty(this._pending)) { 
     this._pending = {}; 
     for (var attr in this.changed) { 
     if (this._pending[attr] || this._silent[attr]) continue; 
     delete this.changed[attr]; 
     } 
     this._previousAttributes = _.clone(this.attributes); 
    } 
    this._changing = false; 
    return this; 
} 

Uso:

model1.set({key: value}, {silent: true}); 
model2.set({key: value}, {silent: true}); 
model3.set({key: value}, {silent: true}); 
collection.saveChanged(); 

RE. RESTfulness. No es del todo correcto hacer un PUT al punto final de la colección para cambiar 'algunos' de sus registros. Técnicamente, un PUT debería reemplazar toda la colección, aunque hasta que mi aplicación realmente necesite reemplazar una colección completa, me complace tomar el enfoque pragmático.

1

Puede definir un nuevo recurso para lograr este tipo de comportamiento, puede llamarlo MyModelBatch.

es necesario implementar un nuevo recurso en que lado del servidor que es capaz de digerir una Array de modelos y ejecutar la acción apropiada: CREATE, UPDATE y DESTROY.

También es necesario implementar un Model en su lado del cliente con una red troncal atributo que es la gama de modelos y una especial url que no haga uso del id.

Sobre el volver a hacer cosa que sugiero que trate de tener una vista por cada modelo por lo que habrá tanto renders como modelos han cambiado pero habrá detalle vuelve a renderizar sin duplicación.

+0

Gracias. Según su sugerencia de usar un modelo temporal, noté que Backbone.sync solo mira una propiedad de url de modelos y el método de JSON, por lo que en lugar de usar un modelo en realidad, podemos simplemente falsificar uno para la sincronización. ToJSON solo devuelve una copia de los atributos de los modelos que se van a codificar, por lo que podemos usar esto para devolver nuestra matriz de modelos. Ver mi respuesta – hacklikecrack

Cuestiones relacionadas