我最终用几种方法来增强 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;
}
Usage:
model1.set({key: value}, {silent: true});
model2.set({key: value}, {silent: true});
model3.set({key: value}, {silent: true});
collection.saveChanged();
关于。 RESTful.. 对集合的端点执行 PUT 来更改其“某些”记录是不太正确的。从技术上讲,PUT 应该替换整个集合,但在我的应用程序实际上需要替换整个集合之前,我很乐意采取务实的方法。