一次保存多个主干模型
Save several Backbone models at once
我有一个包含大量模型的骨干集合。
每当在模型上设置特定属性并保存该属性时,都会触发大量计算并重新呈现 UI。
但是,我希望能够一次在多个模型上设置属性,并且仅在全部设置后进行保存和重新渲染。当然,我不想为一个操作发出多个 http 请求,绝对不想重新渲染界面十次。
我希望在 Backbone.Collection 上找到一种保存方法,该方法可以找出哪些模型具有 Changed((,将它们作为 json 组合在一起并发送到后端。 然后,重新呈现可以由集合上的事件触发。没有这样的运气。
这似乎是一个非常普遍的要求,所以我想知道为什么 Backbone 没有实现。 这是否违背了 RESTful 架构,将多个内容保存到单个端点?如果是这样,那又怎样?发出 1000 个请求来保留 1000 个小项目是不切实际的。
那么,使用我自己的保存方法来增强 Backbone.Collection 的唯一解决方案是迭代其所有模型并为所有已更改的模型构建 json 并将其发送到后端? 或者有人有更整洁的解决方案(或者我只是错过了什么!
我最终用几种方法来增强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(( 方法,因此使用 backbone 的 hasChanged(( 来标记等待保存的模型是有意义的。当然,如果您出于其他目的静默地更改模型,这将有问题 - collection.saveChanged(( 也会保存这些模型,因此值得考虑设置替代标志。
无论如何,如果我们这样做,在保存时,我们需要确保 Backbone 现在认为模型没有更改(不触发其更改事件(,因此我们需要手动操作模型,就好像它没有被更改一样。 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();
再。安息..对集合的端点执行 PUT 以更改其"某些"记录是不完全正确的。 从技术上讲,PUT应该替换整个集合,尽管在我的应用程序真正需要替换整个集合之前,我很乐意采取务实的方法。
您可以定义一个新资源来完成这种行为,您可以将其称为MyModelBatch
。
您需要在服务器端实现一个新资源,该资源能够消化模型Array
并执行正确的操作:CREATE
、UPDATE
和DESTROY
。
此外,您还需要在 Backbone 客户端中实现一个Model
,其中包含一个属性,即模型数组和一个不使用id
的特殊url
。
的事情,我建议您尝试每个模型都有一个视图,这样渲染的渲染量将与模型更改的渲染量一样多,但它们将是细节重新渲染而不会重复。
这就是我想出的。
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);
}
});
- 将JavaScript变量保存到Rails模型
- 一次保存多个主干模型
- 保存两个模型(属于第三个模型)和一个提交
- 主干模型:保存时保留集合
- Ember.js模型保存不向服务器发送数据
- 骨干 js 模型 保存不止一次
- 扩展主干模型保存
- 主干模型保存阻止 UI
- 骨干模型保存问题
- 主干模型保存:将字段作为“模型”子对象发送
- 测试Backbone.js模型保存使用Sinon不调用成功回调
- 使用相同的ng模型保存动态生成的输入网格
- 如何在cakeHP中为关联模型保存多个字段
- 如何使用主干模型保存发送额外的数据到服务器
- 主干模型保存
- 主干模型保存和回调问题
- 模型.保存设置url并在成功时进行回调
- 猫鼬模型保存回调的问题
- 如何将模型保存到数据库
- 主干:模型保存上的两个更改事件