我有一个带有模型负载的Backbone集合。
只要在模型上设置并保存了特定属性,就会触发大量计算,并且UI会重新渲染。
但是,我希望能够一次在多个模型上设置属性,并且仅在它们全部设置后才进行保存和重新渲染。当然,我不想对一个操作发出多个http请求,并且绝对不想将接口(interface)重新渲染十次。
我希望在Backbone.Collection上找到一个保存方法,该方法可以计算出哪些模型具有hasChanged(),将它们作为json打包并发送到后端。然后可以通过集合上的事件触发重新渲染。没有这种运气。
这似乎是一个很常见的要求,所以想知道为什么Backbone无法实现。这是否与RESTful体系结构背道而驰,以将多个内容保存到单个端点?如果是这样,那又如何?发出1000个请求以保留1000个小项目是不现实的。
因此,用我自己的save方法扩展Backbone.Collection的唯一解决方案是迭代所有模型并为所有已更改模型构建json并将其发送到后端的唯一解决方案吗?还是有人有一个更整洁的解决方案(或者我只是错过了什么!)?
最佳答案
我最终使用两种方法来增强Backbone.Collection。
saveChangeMethod创建一个虚拟模型以传递给Backbone.sync。模型需要的所有主干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;
}
尽管这是对 Backbone ``更改模型''范式的预期用途的轻微滥用,但批处理的整个重点是,我们不希望在更改模型时发生任何事情(即触发任何事件)。
因此,我们必须将{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();
关于。 RESTfulness ..对集合的端点执行PUT更改其记录的“某些”是不正确的。从技术上讲,PUT应该替换整个集合,尽管在我的应用程序实际需要替换整个集合之前,我很乐于采用务实的方法。