主干.js - 在以前的保存问题POST(create)而不是PUT(update)请求之前保存模型时出现问题

Backbone.js - problem when saving a model before previous save issues POST(create) instead of PUT(update) request

本文关键字:问题 保存 请求 update PUT 模型 create js 主干 POST      更新时间:2023-09-26

我使用 Backbone 开发了一个很好的丰富应用程序界面.js用户可以非常快速地添加对象,然后通过简单地按 Tab 键转到相关字段来开始更新这些对象的属性。我遇到的问题是,有时用户将服务器打到初始保存,我们最终保存了两个对象。

如何重现此问题的示例如下所示:

  1. 用户单击"添加人员"按钮,我们将其添加到 DOM 中,但尚未保存任何内容,因为我们还没有任何数据。

    person = new Person();

  2. 用户在"名称"字段中输入一个值,然后按 Tab 键退出该值(名称字段失去焦点)。这将触发保存,以便我们在服务器上更新模型。由于模型是新的,Backbone.js将自动向服务器发出POST(创建)请求。

    person.set ({ name: 'John' });

    person.save(); // create new model

  3. 然后,用户非常快速地键入他们已按 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;
    }
  }
})();

这样做的原因如下:

  1. 当模型上的更新或创建请求方法仍在执行时,下一个请求只是放在队列中,以便在调用错误或成功的回调之一时进行处理。

  2. 请求
  3. 时的属性存储在属性数组中,并传递给下一个保存请求。 因此,这意味着当服务器使用第一个请求的更新模型进行响应时,排队请求中的更新属性不会丢失。

我已经上传了一个可以在这里分叉的要点。

一个轻量级的解决方案是猴子修补 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 方法来实现相同的效果,以便此模型同步。 这样做会强制每个调用等待前一个调用完成。

另一种选择是在用户归档内容时只进行设置,并在最后进行一次保存。这也减少了应用程序发出的请求量。