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.
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