我最终用几种方法来增加 Backbone.Collection 来处理这个问题。
saveChangeMethod 创建一个要传递给 Backbone.sync 的虚拟模型。模型需要的所有主干同步方法是它的 url 属性和 toJSON 方法,所以我们可以轻松地敲掉它。
在内部,模型的 toJSON 方法仅返回其属性的副本(发送到服务器),因此我们可以愉快地使用仅返回模型数组的 toJSON 方法。Backbone.sync 对此进行了字符串化,它只为我们提供了属性数据。
成功时, saveChanged 会触发要处理一次的集合上的事件。已经添加了一些代码,使其针对任何批次模型中已更改的每个属性触发一次特定事件。
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);
}
然后,我们只需要集合上的 getChanged() 方法。这将返回一个具有 2 个属性的对象、一个已更改模型的数组和一个标记哪些属性已更改的对象:
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;
}
尽管这是对主干“更改模型”范式的预期用途的轻微滥用,但批处理的全部意义在于,当模型更改时,我们不希望发生任何事情(即触发任何事件)。
因此,我们必须将 {silent: true} 传递给模型的 set() 方法,因此使用主干的 hasChanged() 来标记等待保存的模型是有意义的。当然,如果您出于其他目的默默地更改模型,这将是有问题的 - collection.saveChanged() 也会保存这些,因此值得考虑设置一个替代标志。
无论如何,如果我们这样做,在保存时,我们需要确保主干现在认为模型没有改变(没有触发它们的更改事件),所以我们需要手动操作模型,就好像它没有改变一样被改变了。saveChanged() 方法迭代我们更改的模型并在模型上调用这个 changeSilently() 方法,这基本上只是 Backbone 的 model.change() 方法,没有触发器:
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;
}
用法:
model1.set({key: value}, {silent: true});
model2.set({key: value}, {silent: true});
model3.set({key: value}, {silent: true});
collection.saveChanged();
关于。RESTful .. 对集合的端点执行 PUT 以更改其“某些”记录是不太正确的。从技术上讲,一个 PUT 应该替换整个集合,但在我的应用程序真正需要替换整个集合之前,我很乐意采用务实的方法。