主干.js - 在以前的保存问题POST(create)而不是PUT(update)请求之前保存模型时出现问题
Backbone.js - problem when saving a model before previous save issues POST(create) instead of PUT(update) request
我使用 Backbone 开发了一个很好的丰富应用程序界面.js用户可以非常快速地添加对象,然后通过简单地按 Tab 键转到相关字段来开始更新这些对象的属性。我遇到的问题是,有时用户将服务器打到初始保存,我们最终保存了两个对象。
如何重现此问题的示例如下所示:
-
用户单击"添加人员"按钮,我们将其添加到 DOM 中,但尚未保存任何内容,因为我们还没有任何数据。
person = new Person();
-
用户在"名称"字段中输入一个值,然后按 Tab 键退出该值(名称字段失去焦点)。这将触发保存,以便我们在服务器上更新模型。由于模型是新的,Backbone.js将自动向服务器发出POST(创建)请求。
person.set ({ name: 'John' });
person.save(); // create new model
-
然后,用户非常快速地键入他们已按 Tab 键进入的年龄字段,输入 20 并按 Tab 键转到下一个字段(因此年龄会失去焦点)。这将再次触发保存,以便我们在服务器上更新模型。
person.set ({ age: 20 });
person.save(); // update the model
因此,在这种情况下,我们希望一个 POST 请求创建模型,一个 PUT 请求更新模型。
但是,如果第一个请求仍在处理中,并且在上面第 3 点中的代码运行之前我们没有响应,那么我们实际得到的是两个 POST 请求,因此创建了两个对象而不是一个。
所以我的问题是是否有一些最佳实践方法来处理这个问题和 Backbone.js?或者,Backbone .js是否应该有一个用于保存操作的队列系统,以便在该对象的上一个请求成功/失败之前不会发送一个请求?或者,我应该构建一些东西来优雅地处理这个问题,要么只发送一个创建请求而不是多个更新请求,也许使用某种限制,或者检查 Backbone 模型是否在请求的中间并等待该请求完成。
您对如何处理此问题的建议将不胜感激。
我很高兴尝试实现某种排队系统,尽管您可能需要忍受我的代码,这些代码的格式不如现有的代码库!
我已经测试并设计了一个补丁解决方案,灵感来自在此线程中发帖的@Paul和@Julien。 这是代码:
(function() {
function proxyAjaxEvent(event, options, dit) {
var eventCallback = options[event];
options[event] = function() {
// check if callback for event exists and if so pass on request
if (eventCallback) { eventCallback(arguments) }
dit.processQueue(); // move onto next save request in the queue
}
}
Backbone.Model.prototype._save = Backbone.Model.prototype.save;
Backbone.Model.prototype.save = function( attrs, options ) {
if (!options) { options = {}; }
if (this.saving) {
this.saveQueue = this.saveQueue || new Array();
this.saveQueue.push({ attrs: _.extend({}, this.attributes, attrs), options: options });
} else {
this.saving = true;
proxyAjaxEvent('success', options, this);
proxyAjaxEvent('error', options, this);
Backbone.Model.prototype._save.call( this, attrs, options );
}
}
Backbone.Model.prototype.processQueue = function() {
if (this.saveQueue && this.saveQueue.length) {
var saveArgs = this.saveQueue.shift();
proxyAjaxEvent('success', saveArgs.options, this);
proxyAjaxEvent('error', saveArgs.options, this);
Backbone.Model.prototype._save.call( this, saveArgs.attrs, saveArgs.options );
} else {
this.saving = false;
}
}
})();
这样做的原因如下:
当模型上的更新或创建请求方法仍在执行时,下一个请求只是放在队列中,以便在调用错误或成功的回调之一时进行处理。
请求时的属性存储在属性数组中,并传递给下一个保存请求。 因此,这意味着当服务器使用第一个请求的更新模型进行响应时,排队请求中的更新属性不会丢失。
我已经上传了一个可以在这里分叉的要点。
一个轻量级的解决方案是猴子修补 Backbone.Model.save,因此您只会尝试创建一次模型; 任何进一步的保存都应推迟到模型具有 ID 时。 这样的东西应该有效吗?
Backbone.Model.prototype._save = Backbone.Model.prototype.save;
Backbone.Model.prototype.save = function( attrs, options ) {
if ( this.isNew() && this.request ) {
var dit = this, args = arguments;
$.when( this.request ).always( function() {
Backbone.Model.prototype._save.apply( dit, args );
} );
}
else {
this.request = Backbone.Model.prototype._save.apply( this, arguments );
}
};
我有一些代码称为EventedModel:
EventedModel = Backbone.Model.extend({
save: function(attrs, options) {
var complete, self, success, value;
self = this;
options || (options = {});
success = options.success;
options.success = function(resp) {
self.trigger("save:success", self);
if (success) {
return success(self, resp);
}
};
complete = options.complete;
options.complete = function(resp) {
self.trigger("save:complete", self);
if (complete) {
return complete(self, resp);
}
};
this.trigger("save", this);
value = Backbone.Model.prototype.save.call(this, attrs, options);
return value;
}
});
您可以将其用作主干模型。但它会触发保存和保存:完成。你可以稍微提升一下:
EventedSynchroneModel = Backbone.Model.extend({
save: function(attrs, options) {
var complete, self, success, value;
if(this.saving){
if(this.needsUpdate){
this.needsUpdate = {
attrs: _.extend(this.needsUpdate, attrs),
options: _.extend(this.needsUpdate, options)};
}else {
this.needsUpdate = { attrs: attrs, options: options };
}
return;
}
self = this;
options || (options = {});
success = options.success;
options.success = function(resp) {
self.trigger("save:success", self);
if (success) {
return success(self, resp);
}
};
complete = options.complete;
options.complete = function(resp) {
self.trigger("save:complete", self);
//call previous callback if any
if (complete) {
complete(self, resp);
}
this.saving = false;
if(self.needsUpdate){
self.save(self.needsUpdate.attrs, self.needsUpdate.options);
self.needsUpdate = null;
}
};
this.trigger("save", this);
// we are saving
this.saving = true;
value = Backbone.Model.prototype.save.call(this, attrs, options);
return value;
}
});
(未经测试的代码)
第一次保存调用时,它将正常保存记录。如果您快速执行新的保存,它将缓冲该调用(将不同的属性和选项合并到单个调用中)。第一次保存成功后,继续进行第二次保存。
作为上述答案的替代方法,您可以通过重载 backbone.sync 方法来实现相同的效果,以便此模型同步。 这样做会强制每个调用等待前一个调用完成。
另一种选择是在用户归档内容时只进行设置,并在最后进行一次保存。这也减少了应用程序发出的请求量。
- 保存到通过Javascript解析的问题
- 如何使用 2 个表单集并保存它们(发布方法问题)
- 动态注入时保存文本区域值的夏季注释问题
- 与将 Jquery 保存在单独的.js文件中相关的问题
- 保存包的 Breezejs 问题
- 鸟舍 WEB API 保存高分辨率问题
- 保存、从 cookie 加载时出现问题
- 骨干模型保存问题
- 组合两个脚本以在所有浏览器上保存Google图表时出现问题
- 拖放保存定位问题
- 保存到cookie的常见问题的简单隐藏/显示
- 博客数据保存问题
- 角度拖放保存在数据库中的位置和屏幕大小调整问题
- 在完整日历中保存事件时出现问题
- 在php中保存上传的图像时遇到问题
- 尝试从URL下载并用node.js保存到文件中.这是我的问题
- 在刷出下保存自定义对象到内存的问题
- Javascript到PHP的日期转换在时区中丢失.也许mysql保存问题
- Facebook评论社交插件-问题保存/访问当前URL的评论
- 将日期时间问题保存到用户的本地日期时间