Ho finito per aumentare Backbone.Collection con un paio di metodi per gestire questo.
saveChangeMethod crea un modello fittizio da passare a Backbone.sync. Il metodo di sincronizzazione di tutti i backbone di un modello è la sua proprietà url e il metodo toJSON, quindi possiamo facilmente metterlo in discussione.
Internamente, il metodo toJSON di un modello restituisce solo una copia dei suoi attributi (da inviare al server), quindi possiamo tranquillamente usare solo un metodo toJSON che restituisce semplicemente l'array di modelli. Backbone.sync lo stringe, il che ci dà solo i dati degli attributi.
In caso di successo, saveChanged spegne gli eventi sulla raccolta da gestire una volta. Hanno buttato un po 'di codice che lo fa sparare eventi specifici una volta per ciascuno degli attributi che sono cambiati in uno dei modelli del lotto.
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);
}
Abbiamo poi solo bisogno il metodo getChanged() su una collezione. Ciò restituisce un oggetto con 2 proprietà, una matrice dei modelli modificati e un contenenti contrassegni oggetto attributi sono cambiati:
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;
}
Anche se questo è piccolo abuso di destinazione del 'mutato modello' dorsali paradigma, il punto del batching è che non vogliamo che accada qualcosa (cioè eventi da sparare) quando un modello viene cambiato.
Dobbiamo quindi passare {silent: true} al metodo set() del modello, quindi è logico utilizzare hasChanged() del backbone per contrassegnare i modelli in attesa di essere salvati. Ovviamente questo sarebbe problematico se stessimo cambiando modelli in silenzio per altri scopi - collection.saveChanged() salverebbe anche questi, quindi vale la pena prendere in considerazione l'impostazione di un flag alternativo.
In ogni caso, se stiamo facendo in questo modo, al momento del salvataggio, dobbiamo assicurarci che backbone pensi che i modelli non siano cambiati (senza attivare i loro eventi di modifica), quindi dobbiamo manipolare manualmente il modello come se non era stato cambiato. Il saveChanged() metodo itera oltre i nostri modelli cambiato e chiama questo metodo changeSilently() sul modello, che è fondamentalmente solo model.change di Backbone() metodo senza i trigger:
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;
}
Usage:
model1.set({key: value}, {silent: true});
model2.set({key: value}, {silent: true});
model3.set({key: value}, {silent: true});
collection.saveChanged();
RE. RESTfulness .. Non è del tutto corretto fare un PUT all'endpoint della collezione per cambiare "alcuni" dei suoi record. Tecnicamente un PUT dovrebbe sostituire l'intera collezione, anche se fino a quando la mia applicazione non avrà mai realmente bisogno di sostituire un'intera collezione, sono felice di adottare l'approccio pragmatico.
Correlato: 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