“如何”将整个集合保存在Backbone.js-Backbone.sync或jQuery.ajax中?


81

我很清楚这是可以做到的,并且我已经研究了很多地方(包括:保存整个收藏集的最佳实践?)。但是我仍然不清楚它到底是用代码编写的吗?(该帖子以英语进行了解释。最好有一个JavaScript特定的解释:)

假设我有一个模型集合-模型本身可能具有嵌套集合。我已经覆盖了父集合的toJSON()方法,并且得到了一个有效的JSON对象。我希望“保存”整个集合(对应的JSON),但是主干似乎并没有内置该功能。

var MyCollection = Backbone.Collection.extend({
model:MyModel,

//something to save?
save: function() {
   //what to write here?
 }

});

我知道您必须在某处说:

Backbone.sync = function(method, model, options){
/*
 * What goes in here?? If at all anything needs to be done?
 * Where to declare this in the program? And how is it called?
 */
}

一旦完成了“视图”的处理,它便负责告诉集合在服务器上“保存”自身(能够处理批量更新/创建请求)。

出现的问题:

  1. 如何/以何种方式编写代码以“将所有内容连接在一起”?
  2. 回调的“正确”位置是什么,以及如何指定“成功/错误”回调?我的意思是语法吗?我不清楚在主干中注册回调的语法...

如果确实是一项棘手的工作,那么我们可以在视图中调用jQuery.ajax并通过this.successMethodthis.errorMethod作为成功/错误回调吗?能行吗

我需要与主干的思维方式保持同步-我知道我肯定缺少某些东西,可以同步整个收藏。


您的服务器端代码能否将其作为单个请求?换句话说,整个顶级集合,所有模型和嵌套集合都是单个JSON数据包吗?或者,您是否需要分别保存每个模型?编辑:啊,仔细阅读,服务器能够“批量更新/创建”
爱德华·M·史密斯,

@爱德华:是的!明确指出了这一点,因为它通常是一个关注点,但在这种情况下不是这样:)
博士

那么,服务器期望接收的数据的结构是什么?
爱德华·M·史密斯,

@爱德华:数据的结构重要吗?注释中无法进行格式设置,但这是这样的:[{postId:1,标签:[{id:1,name:“ a”},{id:2,name:“ b”}]}]]基本上每个“ postId”可以具有一组标签/数组,它们本身就是对象。可能有很多这样的帖子...除非我遗漏了某些东西,否则我认为数据格式与当前问题没有任何关系
博士

Answers:


64

我立即想到的是不覆盖Backbone.Collection上save方法的方法,而是将集合包装在另一个Backbone.Model中并在其上覆盖toJSON方法。然后,Backbone.js将模型视为单一资源,而您不必费解Backone的思考方式。

请注意,Backbone.Collection具有toJSON方法,因此您的大部分工作都已为您完成。您只需要将包装程序Backbone.Model的toJSON方法代理到Backbone.collection。

var MyCollectionWrapper = Backbone.Model.extend({
url: "/bulkupload",

//something to save?
toJSON: function() {
    return this.model.toJSON(); // where model is the collection class YOU defined above
 }

});

3
我查看了源代码,似乎Collections没有save方法,所以覆盖应该不是问题(如果确实有save方法,世界会容易很多:)
PhD

+1的包装创意-干净而甜美。完全没有想到。我当时在想,也许我将不得不直接调用Backboard.sync并仅通过集合来代替“模型”参数。尽管...有什么想法,但需要为模型指定一个url才能运行。由于sync方法在内部仅调用getURL(model)模型参数,并且也不执行任何类型的比较...似乎是设计目的
PhD

3
同意-非常优雅。但是,我遇到了这种解决方案的问题:我的包装器模型始终是新的,因此当我save()时,它始终是POST。我已经检索了我的数据,所以当我保存()时,它应该是一个PUT。我想我可以硬编码isNew()= false或设置一个伪造的ID,但这似乎不是一个很好的解决方案。你有什么建议吗?
斯科特·斯威策

1
答案很干净,很高兴看到您如何实例化CollectionWrapper。
安东尼

是的,它也为我工作,并为+1尽了最大努力,但是如果我想将两个集合发送到服务器,该怎么办?
CodeNotFound 2013年

25

一个非常简单的...

Backbone.Collection.prototype.save = function (options) {
    Backbone.sync("create", this, options);
};

...将为您的馆藏提供保存方法。请记住,无论更改了什么,它将始终将所有集合的模型发布到服务器。options只是普通的jQuery ajax选项。


4
似乎正确。可能会添加return Backbone.sync..更多Backbonish。
伊夫·阿姆瑟勒姆

我认为-对于类型-更新会比创建更好...顺便说一句。您可以覆盖模型或集合的toJSON,因此可以调节发送到服务器的内容...(通常只是需要的id属性)在Backbone.Relational中,您还可以设置要添加到json格式的内容。
inf3rno

1
Backbone.sync预计会“创建”,请参见
ribs.js / org / docs /

8

我最终只是拥有一个类似“保存”的方法,并在其中调用了$ .ajax。它为我提供了更多控制权,而无需像@brandgonesurfing所建议的那样添加包装类(尽管我绝对喜欢这个主意:)如前所述,因为我已经重写了collection.toJSON()方法,因此我着手做的就是使用它在ajax电话中...

希望这对偶然发现它的人有所帮助...


3
您最好调用Backbone.ajax(无论如何它都是jQuery的代理,但它使其更易于维护)
developerbmw

5

这实际上取决于客户端和服务器之间的合同。这是一个简化的CoffeeScript示例,其中与的PUT将/parent/:parent_id/children用PUT中的{"children":[{child1},{child2}]}内容替换父母的孩子并返回{"children":[{child1},{child2}]}

class ChildElementCollection extends Backbone.Collection
  model: Backbone.Model
  initialize: ->
    @bind 'add', (model) -> model.set('parent_id', @parent.id)

  url: -> "#{@parent.url()}/children" # let's say that @parent.url() == '/parent/1'
  save: ->
    response = Backbone.sync('update', @, url: @url(), contentType: 'application/json', data: JSON.stringify(children: @toJSON()))
    response.done (models) => @reset models.children
    return response

这是一个非常简单的示例,您可以做更多的事情……它实际上取决于执行save()时数据所处的状态,将其传送到服务器所需的状态以及服务器提供的状态背部。

如果服务器的PUT正常[{child1},{child2],则Backbone.sync行可能更改为response = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json')


选项“ url”的属性在这里不是必需的=)
Sergey Kamardin

5

答案取决于您要对服务器端的集合执行的操作。

如果必须在发布后发送其他数据,则可能需要包装模型或关系模型

使用包装器模型,您始终必须编写自己的parse方法:

var Occupants = Backbone.Collection.extend({
    model: Person
});

var House = Backbone.Model.extend({
    url: function (){
        return "/house/"+this.id;
    },
    parse: function(response){
        response.occupants = new Occupants(response.occupants)
        return response;
    }
});

我认为关系模型更好,因为您可以更轻松地配置它们,并且可以使用includeInJSON选项进行调节,将哪些属性放入发送到rest服务的json中。

var House = Backbone.RelationalModel.extend({
    url: function (){
        return "/house/"+this.id;
    },
    relations: [
        {
            type: Backbone.HasMany,
            key: 'occupants',
            relatedModel: Person,
            includeInJSON: ["id"],
            reverseRelation: {
                key: 'livesIn'
            }
        }
    ]
});

如果您不发送其他数据,则可以同步集合本身。在这种情况下,您必须向集合(或集合原型)添加一个save方法:

var Occupants = Backbone.Collection.extend({
    url: "/concrete-house/occupants",
    model: Person,
    save: function (options) {
        this.sync("update", this, options);
    }
});

3

我也对Backbone集合没有内置保存功能感到惊讶。这是我在我的骨干收藏中所做的事情。我绝对不想遍历集合中的每个模型并独立保存。另外,我在使用Node的后端上使用Backbone,因此会覆盖本机Backbone.sync以保存到我的小型项目中的平面文件中-但代码应该几乎相同:

    save: function(){                                                                                                                                                                                                                                                                                                                                                     
      Backbone.sync('save', this, {                                                                                                                                                                                                                                                                                                                                     
        success: function(){                                                                                                                                                                                                                                                                                                                                          
          console.log('users saved!');                                                                                                                                                                                                                                                                                                                              
        }                                                                                                                                                                                                                                                                                                                                                             
      });                                                                                                                                                                                                                                                                                                                                                               
    }

仅传递选项也很有意义,例如save: function (options) { Backbone.sync('save', this, options); }
Matt Fletcher

3

我知道的旧线程,最终要执行的操作如下:

Backbone.Collection.prototype.save = function (options) {
            // create a tmp collection, with the changed models, and the url
            var tmpCollection = new Backbone.Collection( this.changed() );
            tmpCollection.url = this.url;
            // sync
            Backbone.sync("create", tmpCollection, options);
        };
        Backbone.Collection.prototype.changed = function (options) {
            // return only the changed models.
            return this.models.filter( function(m){
                return m.hasChanged()
            });
        };
// and sync the diffs.
self.userCollection.save();

相当前卫:)


2

这是一个简单的例子:

var Books = Backbone.Collection.extend({
model: Book,
url: function() {
  return '/books/';
},
save: function(){
  Backbone.sync('create', this, {
    success: function() {
      console.log('Saved!');
    }
  });
 }
});

当您在集合上调用save()方法时,它将向定义的URL发送一个PUT方法请求。


我想知道是否可以帮助解决我的收藏保存问题,现在jsfiddle.net/kyllle/f1h4cz7f/3 toJSON()更新每个模型,但是保存似乎没有任何数据?
造型师

1

我会尝试类似的东西:

var CollectionSync = function(method, model, [options]) {
    // do similar things to Backbone.sync
}

var MyCollection = Backbone.Collection.extend({
    sync: CollectionSync,
    model: MyModel,
    getChanged: function() {
        // return a list of models that have changed by checking hasChanged()
    },
    save: function(attributes, options) {
        // do similar things as Model.save
    }
});

https://stackoverflow.com/a/11085198/137067


1

可接受的答案是非常好的,但是我可以更进一步,为您提供代码,以确保为侦听器触发适当的事件,同时还允许您传递选项ajax事件回调:

save: function( options ) {
  var self = this;

  var success = options.success;
  var error = options.error;
  var complete = options.complete;

  options.success = function( response, status, xhr ) {
    self.trigger('sync', self, response, options);
    if (success) return success.apply(this, arguments);
  };

  options.error = function( response, status, xhr ) {
    self.trigger('error', self, response, options);
    if (error) return error.apply(this, arguments);
  };

  options.complete = function( response, status, xhr ) {
    if (complete) return complete.apply(this, arguments);
  }

  Backbone.sync('create', this, options);
}

0

对于2017年仍在使用ribs.js的任何人,可接受的答案均无效。

尝试删除包装模型中的toJSON()重写,并在实例化模型包装时在集合上调用toJSON。

new ModelWrapper(Collection.toJSON());
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.