我的网络应用程序中有拖放功能。当用户在拖放后释放鼠标时,对象的位置会使用模型上的 Backbone 的 save() 方法保存到服务器。当服务器响应时,它会在具有返回属性的模型上触发 set()。但是,在服务器处理请求时,用户可能已经再次将对象拖到不同的位置。这会导致问题,因为来自服务器的响应现在将覆盖浏览器中对象的设置。
有没有办法阻止Backbone在save()之后从服务器获得响应后执行set()?
我的网络应用程序中有拖放功能。当用户在拖放后释放鼠标时,对象的位置会使用模型上的 Backbone 的 save() 方法保存到服务器。当服务器响应时,它会在具有返回属性的模型上触发 set()。但是,在服务器处理请求时,用户可能已经再次将对象拖到不同的位置。这会导致问题,因为来自服务器的响应现在将覆盖浏览器中对象的设置。
有没有办法阻止Backbone在save()之后从服务器获得响应后执行set()?
之前在做系统时也有类似的用例,尽管这更麻烦,因此我们需要真正覆盖模型set()
功能。尽管对于这种情况,有几个相对简单的方法可用。
您可以覆盖模型parse()函数。或者您可以在调用abort()
返回的 jqXHR 对象上调用一个save()
。
查看“等待”选项,默认情况下它应设置为 false,这意味着您的模型字段应在调用 save() 后立即设置。您可以设置 {wait:true} 以使主干在设置更新的模型字段值之前等待服务器回复。
从您描述的行为来看,您的应用程序中的等待选项似乎设置为 true。
更多细节:http ://backbonejs.org/#Model-save
要记住的另一件重要的事情是主干只设置您从被调用的网络服务返回的更新字段的子集,因此如果您不返回任何内容,则不会设置任何内容。
Model.save
将在实际设置之前调用Model.parse
使用相同参数接收到的数据。options
您可以Model.save
使用自定义标志(比如说position: true
)进行调用,指示Model.parse
可以丢弃服务器返回的内容。
例如,假设您的坐标x
是y
var M = Backbone.Model.extend({
parse: function(data, options) {
if (options && options.position)
data = _.omit(data, 'x', 'y');
return data;
}
});
var m = new M();
m.save({x: 10, y:10}, {position: true});
m.save({x: 20, y:20}, {position: true});
我们已经使用方法来冻结 UI,直到执行 ajax 请求。我们向模型添加了两个额外的事件冻结/解冻,这些事件在保存之前和从服务器接收到答案之后触发。
尝试覆盖 '.sync()' 方法。Backbone.save() 在覆盖您传入的成功回调后委托给 Backbone.sync()。这就是模型更新的来源。
从 Backbone.save() 方法的来源:
var success = options.success;
options.success = function(resp) {
// Ensure attributes are restored during synchronous saves.
model.attributes = attributes;
var serverAttrs = model.parse(resp, options);
if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
return false;
}
if (success) success(model, resp, options);
model.trigger('sync', model, resp, options);
};
wrapError(this, options);
method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
if (method === 'patch') options.attrs = attrs;
xhr = this.sync(method, this, options);
您可以在调用“.save()”时传递自定义选项,然后查找该选项并覆盖“.sync()”中的 options.success 回调。像这样的东西,也许?
MyModel = Backbone.Model.extend({
sync: function (method, context, options) {
if (options.skipUpdateOnResponse) {
options.success = myCusomSuccessFunction;
}
}
});
myModel = new MyModel();
myModel.save({...}, {skipUpdateOnResponse: true})
服务器响应什么属性?Backbone 使用的默认成功回调执行以下操作:
if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { ... }
按照这个逻辑,如果你的服务器没有返回一个对象,set
就不应该在你的模型上调用。
或者,如果您修改服务器响应以使其不返回传入的位置信息(这是您的问题的根本原因,并且不了解您的要求,也似乎有点多余)这些属性不应该得到被调用时set
被覆盖。
除非有这些选项,sync
否则像@Uselessinfo 建议的那样覆盖或放入一些诡计parse
可能是您的下一个最佳选择。
在模型上使用 validate 方法:
validate: function(attrs) {
var errors = [];
if (attrs.position !== this.get('position')) {
errors.push('Outdated info!');
}
return _.any(errors) ? errors : null;
}
这样,您可以将服务器响应与模型中的现有数据进行比较。如果服务器响应与模型中的数据不同,则返回错误并且set
不会调用该方法。这可能需要wait: true
在save
方法选项中。使用这种方法,我认为您应该在进行实际保存之前在模型上设置位置。
validate 在保存之前调用,但如果 {validate:true} 也可以在设置之前调用
骨干网保存成功回调:
options.success = function(resp, status, xhr) {
done = true;
var serverAttrs = model.parse(resp, xhr);
if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
if (!model.set(serverAttrs, options)) return false;
if (success) success(model, resp, options);
model.trigger('sync', model, resp, options);
};
因此,它总是在调用触发“同步”之前和调用用户成功函数之前调用 set。没有办法阻止 Backbone 进行设置,但是有一种方法可以解决您的问题。您可以像这样覆盖模型中的“设置”功能:
set: function() {
if (this.skipSetOperation()) return false;
return Backbone.Model.prototype.set.apply(this, arguments);
},
skipSetOperation: function() {
//this function should return stage - can model set attributes in this moment or not
}
希望能帮助到你。
我现在使用以下解决方案:
覆盖模型的默认解析方法:
Backbone.Model.prototype.parse = function(resp, options) {
if(options.parse)
return resp;
};
现在,您可以使用属性 'parse' 设置为 false 的 save 方法来防止 ajax 响应覆盖您当前的值:
MyModel.save({}, { parse: false });
为您的保存编写另一个函数,而不是使用保存使用您的函数。
在模型中:
var myModel = Backbone.Model.extend({
savePosition: function(){
var model = this;
$.ajax({
type : "POST",
cache: false,
dataType : 'json',
contentType : 'application/json',
data:JSON.stringify(data),
url : "urlToSave",
success:function(responseText,statusText){
model.trigger('saved',responseText);
},
error : function(error, dataObj, xhr) {
alert('Error');
}
});
}
}
这里查看保存的绑定方法,保存后触发该方法。
this.model.on('saved',function(){
// do call back stuff
});
this.model.savePosition({data:{...}});