我有一个骨干收集与型号的负载。
每当一个特定的属性被设置在一个模型,它被保存,计算的负载断火和UI重新渲染。
但是,我希望能够一次设置多个模型的属性和只能做储蓄和重新描绘,一旦他们所有的设置。 当然,我不想做几个HTTP请求一个操作,绝对不想有重新呈现接口的十倍。
我希望能找到Backbone.Collection)保存方法,将工作哪些型号hasChanged(一起重击他们作为JSON和欢送到后端。 将重新描绘然后可以通过对收集的事件触发。 没有这样的运气。
这似乎是一个很常见的需求,所以很奇怪,为什么骨干网没有实现。 这是否违背一个RESTful架构,以几件事保存到一个终点? 如果是这样,那又怎样? 有没有办法,它的实用性,使1000个请求将持续1000小件物品。
那么,是增加Backbone.Collection用我自己的方法保存在其所有车型,遍历该唯一的解决办法,并建立了JSON所有已更改的那些,并将其关闭到后端? 或者有没有人有一个整洁的溶液(或我只是失去了一些东西!)?
我已经结束了增强Backbone.Collection与一对夫妇的方法来处理这个问题。
该saveChangeMethod创建要传递给Backbone.sync一个假人模型。 所有骨干的同步方法需要从一个模型是其url属性和方法的toJSON,所以我们可以很容易地敲这件事。
在内部,一个模型的toJSON方法只返回一个副本的属性(被发送到服务器),所以我们可以愉快地只使用刚刚返回的车型阵列的toJSON方法。 Backbone.sync stringifies这一点,这给了我们刚才的属性数据。
如果成功,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;
}
虽然这是用途骨干“改变模式”范式的轻微滥用,配料的整点是,我们不希望在一个模型改变任何事情发生(即没有任何事件可以触发关闭)。
因此,我们必须通过{沉默:真正}到模型的set()方法,因此是很有意义使用骨干hasChanged()来标记模型等进行保存。 当然,如果你是为了其他目的默默地改变模式,这将是有问题的 - collection.saveChanged()将保存这些了,所以这是值得考虑设置一个替代标志。
在任何情况下,如果我们正在做这样既节约时,我们需要确保骨干认为现在的模式并没有改变(不触发其变更事件),所以我们需要手动操作模式,如果它有没有被改变。 该saveChanged()在我们改变模型方法进行迭代,并呼吁模式,这基本上是骨干model.change()方法不触发这个changeSilently()方法:
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应该更换整个集合,但直到我的应用程序不断实际上需要更换整个集合,我很高兴地采取务实的做法。
您可以定义一个新的资源来完成这种行为的,可以称之为MyModelBatch
。
您需要实现在你的服务器端建立一个新的资源,是能够消化的Array
型号和执行正确的动作: CREATE
, UPDATE
和DESTROY
。
你也需要实现一个Model
在骨干网客户端与一个属性 ,它是模型的阵列和一个特殊的url
不使用的id
。
关于重新渲染的东西,我建议你尝试有一个查看每个型号所以会有尽可能多呈现为车型已改变,但他们会详细重新呈现不重复。
这是我想出了。
Backbone.Collection.extend({
saveAll: function(models, key, val, options) {
var attrs, xhr, wait, that = this;
var transport = {
url: this.url,
models: [],
toJSON: function () {
return { models: this.models };
},
trigger: function(){
return that.trigger.apply(that, arguments);
}
};
if(models == null){
models = this.models;
}
// Handle both `"key", value` and `{key: value}` -style arguments.
if (key == null || typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
options = _.extend({validate: true}, options);
wait = options.wait;
// After a successful server-side save, the client is (optionally)
// updated with the server-side state.
if (options.parse === void 0) options.parse = true;
var triggers = [];
_.each(models, function(model){
var attributes = model.attributes;
// If we're not waiting and attributes exist, save acts as
// `set(attr).save(null, opts)` with validation. Otherwise, check if
// the model will be valid when the attributes, if any, are set.
if (attrs && !wait) {
if (!model.set(attrs, options)) return false;
} else {
if (!model._validate(attrs, options)) return false;
}
// Set temporary attributes if `{wait: true}`.
if (attrs && wait) {
model.attributes = _.extend({}, attributes, attrs);
}
transport.models.push(model.toJSON());
triggers.push(function(resp){
if(resp.errors){
model.trigger('error', model, resp, options);
} else {
// Ensure attributes are restored during synchronous saves.
model.attributes = attributes;
var serverAttrs = options.parse ? model.parse(resp, options) : resp;
if (wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
return false;
}
model.trigger('sync', model, resp, options);
}
});
// Restore attributes.
if (attrs && wait) model.attributes = attributes;
});
var success = options.success;
options.success = function(resp) {
_.each(triggers, function(trigger, i){
trigger.call(options.context, resp[i]);
});
if (success) success.call(options.context, models, resp, options);
};
return this.sync('create', transport, options);
}
});