Backbone.js部分模型更新

时间:2011-03-11 13:18:16

标签: javascript backbone.js

保存更改时是否可以仅发送模型的修改属性?

顺便说一句,是否有任何“官方”Backbone.js小组/邮件列表可以提出这类问题?

11 个答案:

答案 0 :(得分:46)

Backbone不支持开箱即用,但您拥有实现这一目标的所有工具。 如果您查看Backbone.sync,您将看到它在您的模型上调用了JSON以获取要发送的实际数据。现在你可能需要调整一下,但这是它的要点:

initialize: function(){
  this.dirtyAttributes = {}
},
set: function(attrs, options){
  Backbone.Model.prototype.set.call(this, attrs, options);
  _.extend(this.dirtyAttributes, attrs);
},
toJSON : function(){
  json = this.dirtyAttributes;
  this.dirtyAttributes = {};
  return json;
}

如果你想要一个完整的解决方案,你需要应用相同的逻辑来取消设置,清除,保存等。但我想你会得到如何做到这一点。我把脏属性的重置放在toJSON函数中,但它应该在成功回调中(当调用save时)。

答案 1 :(得分:33)

目前,骨干网不支持将部分模型发送到服务器。这将是一个有趣的补充。

如果您browse the source,您可以看到Backbone.sync(负责与数据存储通信的主干部分)是骨干网中最简单的组件之一,只需在jQuery中包含ajax支持或者Zepto。


更新

启动主干版本0.9.10,本地支持部分模型更新

model.save(attrs, {patch: true})

答案 2 :(得分:6)

更新:启动主干版本0.9.10,本地支持部分更新

model.save(attrs, {patch: true})


直到0.9.9 没有直接编辑backbone.js库文件的方法。只需在应用程序js文件中添加以下代码,并在backbone.js加载后加载它。

//override the Backbone.sync to send only the changed fields for update (PUT) request
var Original_BackboneSync = Backbone.sync;

Backbone.sync = function(method, model, options) {
    /* just handle the data picking logic for update method */
    if (!options.data && model && method == 'update') {
        options.contentType = 'application/json';
        options.data = JSON.stringify(model.changedAttributes() || {});
    }

    //invoke the original backbone sync method
    return Original_BackboneSync.apply(this, arguments);
};

//Tested in Backbone.js 0.9.1

答案 3 :(得分:2)

我尝试了这里建议的几种技术,但最后决定直接修改Backbone.Model和Backbone.sync。我想提供的是一种提供此功能的微创方法,不需要指示我的团队中的开发人员覆盖骨干方法;太容易出错了。我的解决方案只涉及将选项传递给模型的“保存”方法。例如:

//Note that this could be from your view or anywhere you're invoking model.save
saveToModel : function() {
    this.model.save({
        field1 : field1Value,
        field2 : field2Value,
        field3 : field3Value
    }, {partialUpdate : true}
}

现在,为了启用此功能,我对Backbone.Model.save和Backbone.sync进行了一些非常小的修改。这是对Backbone.Model.save的更改:

//If a partialUpdate is required, create a member on the options
//hash called updateAttrs and set it to attrs
if (options.partialUpdate != "undefined" && options.partialUpdate) {
    options.updateAttrs = attrs;
}
//--->>>Put the block above right above the return line
return (this.sync || Backbone.sync).call(this, method, this, options);

这里发生的是,如果将partialUpdate作为选项传递,则会在选项哈希上创建名为updateAttrs的新成员。选项哈希自动传递给Backbone.sync。

对于Backbone.sync,我更改了以下条件:

// Ensure that we have the appropriate request data.
if (!params.data && model && (method == 'create' || method == 'update')) {
    params.contentType = 'application/json';
    params.data = JSON.stringify(model.toJSON());
}

为...

// Ensure that we have the appropriate request data.
if (!params.data && model && (method == 'create' || method == 'update')) {
    params.contentType = 'application/json';

    //If doing a partial model update, then grab the updateAttrs member
    //from options. Will not interfere with line directly below as params.data
    //will have been set.
    params.data = (options.partialUpdate != "undefined" && options.partialUpdate)
                ? params.data = JSON.stringify(options.updateAttrs)
                : params.data = JSON.stringify(model.toJSON());
}

添加额外的条件检查以查看是否设置了partialUpdate,如果是,则将params.data设置为options.updateAttrs。然后将其传递给jquery Ajax方法。

答案 4 :(得分:2)

您可以在Backbone.sync方法中执行此操作,而不是覆盖Model.sync。由于您无法访问model.changedAttributes(),请务必在此方法中始终返回false。

sync: (method, model, options) ->
  if method is "update"
    options.contentType = 'application/json'
    changedData = {}
    for attr in _.keys(options.changes)
      changedData[attr] = model.get(attr)
    options.data = JSON.stringify changedData

  Backbone.sync method, model, options

答案 5 :(得分:1)

如果您需要向仅具有特定属性的服务器发送更新请求,您可以执行类似的操作:

saveAttributes: (attributes, options={}) ->
  data = {}
  _(attributes).each (attribute) =>
    data[attribute] = @get(attribute)

  params =
    data: $.param(data)

  _.extend(params, options)

  Backbone.sync('update', null, params)

有关详情:https://github.com/documentcloud/backbone/pull/573

您可以使用_.extend Backbone.Model.prototype

进行扩展

答案 6 :(得分:1)

这里的大部分答案都是直接或间接修改sync功能。这是我克服这个问题的小技巧:

当您呼叫model.save时,您实际上可以传入第二个参数,该参数将在Backbone尝试呼叫同步时与$.ajax一起传递。我做了像这样的部分更新,更明确地指定了要提交的字段:

/**
 * On user clicking on "mark important"
 */
onMarkImportantBtnClick: function() {
    var marked = this.model.get('UserFeed.marked_important'),
        data = {
            UserFeed: {
                marked_important: !marked
            }
        };
    this.model.save(data, {data: JSON.stringify(data), contentType: 'application/json'});
}

此操作正确更新了我的模型属性,并仅向服务器发送JSON.stringify中提到的数据。此处需要 contentType,更好

这是因为Backbone.sync has these lines,我们通过传递data属性来否定它:

if (!options.data && model && (method == 'create' || method == 'update')) {
    params.contentType = 'application/json';
    params.data = JSON.stringify(model.toJSON());
}

相信此页:https://plus.google.com/103858073822961240171/posts/1gTcu6avmWQ

注意:此模型继承自powmedia的DeepModel以支持嵌套模型属性


修改

自Backbone 0.9.9以来,添加了patch选项,因此此技巧仅适用于以前的版本。

要仅将脏数据提交回您的服务器,请在{patch: true}中提供save,以便

this.model.save(modifiedData, {patch: true});

感谢@Lincoln B指出它。

答案 7 :(得分:0)

使用Jayyy V的(非常好的)答案,我重写了一点,使同步功能采用白名单,这样你就可以给它一组保存的密钥。

var Original_BackboneSync = Backbone.sync;
Backbone.sync = function(method, model, options) {
    /* check if a whitelist was in options */
    if (options.whitelist) {
      options.contentType = 'application/json';
      /* use underscore method for picking only whitelisted attributes to save */
      options.data = JSON.stringify(_.pick(model.attributes, options.whitelist));
    }

    //invoke the original backbone sync method
    return Original_BackboneSync.apply(this, arguments);
};

答案 8 :(得分:0)

以@ Julien的帖子为基础: 您可以将其添加到模型中,它只会发送您传入的属性而不是整个模型。您仍然可以使用save作为默认行为,并且当您希望仅将作为参数传递的属性发送时,可以使用partialSave。我测试了这个,它对我有用。

  partialSave: function(attr, options) { //use this method instead of save()
    this.dirtyAttributes = attr;
    this.save(attr, options);
  },

  toJSON: function() { //overrides Backbone.Model.prototype.toJSON
    if (this.dirtyAttributes) {
      var attr = this.dirtyAttributes;
      this.dirtyAttributes = null;
      return attr;
    }
    return Backbone.Model.prototype.toJSON.apply(this, arguments);
  },

答案 9 :(得分:0)

实际上有一种更简单的方法来实现这个

如果你看看backbone.js第1145行,你会看到

// Ensure that we have the appropriate request data.
    if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
      params.contentType = 'application/json';
      params.data = JSON.stringify(options.attrs || model.toJSON(options));
    }

这意味着您可以通过在选项中添加数据来覆盖xhr的数据部分

由于骨干保存需要model.save([attributes], [options])

但请记住,像id这样的属性可能对正确保存至关重要

示例

model.save( {}, { data: JSON.stringify(data) } ) ; 

所以你应该做这样的事情

var data = { id : model.id , otherAttributes : 'value' }  ;  

var data = model.toJSON () ;
remove data.tempData ; 

最后

model.save( {}, { data : JSON.stringify(data) } );

这对我来说非常好用,可以和xhr的任何骨干一起使用,例如fetch,save,delete,...

与save,sync或toJSON混淆看起来错误

答案 10 :(得分:0)

我创建了扩展模型。

var CModel = Backbone.Model.extend({
    save: function(attributes, options) {
        if(_.isUndefined(options)) {
            options = {};
        }

        var isNeedAttrsRefresh = false,
            basicAttributes = null;

        if(!_.isUndefined(options.fields)) {
            basicAttributes = _.clone(this.attributes);
            var newAttributes = {};
            _.each(this.attributes, function(value, name) {
                if(options.fields.indexOf(name) > -1) {
                    newAttributes[name] = value;
                }
            });
            this.attributes = newAttributes;
            isNeedAttrsRefresh = true;
        }

        this.isSaving = true;
        var result = Backbone.Model.prototype.save.apply(this, arguments);
        this.isSaving = false;

        if(isNeedAttrsRefresh) {
            this.attributes = basicAttributes;
        }

        return result;
    }
});

使用示例:

var CommentModel = CModel.extend({ ... }

允许保存字段:

comment.save(null, {    fields: ['message', 'entry_id', 'module_id', 'parent_id'] });