BackboneJs - 模型更改时保留模型内集合的事件

时间:2016-03-09 07:32:45

标签: backbone.js backbone-events backbone-collections

我在模型中有一个集合,如下图所示:

var itemModel = Backbone.Model.extend({

   defaults:{
      name:"",
      brand:"",
      priceCollection:[]
    }
})

附加到itemModel的更改侦听器也会更改附加到集合的侦听器  在视图中this.listenTo(itemModel.get('priceCollection'),'change',this.dosomething)

问题是,如果通过{{1}为模型提供了一组新属性,只要父模型没有改变,集合上的更改侦听器就可以正常工作 itemModel.get(' priceCollection')上绑定的事件将丢失。

我如何保留此活动?或者我应该在模型每次更改时重新绑定此事件?或者我应该将集合上的侦听器从视图移动到模型并触发自定义Backbone事件吗?

应该注意,这个模型是单身

2 个答案:

答案 0 :(得分:1)

请记住,Backbone假设Collection和Model应该以1:1的比例映射到服务器端资源。它明确了解API布局和数据结构 - 请参阅Model.urlModel.urlRootCollection.url

提案

你说模特是单身人士。在这种情况下,我建议分别维护模型和集合。

由于SomeModel没有伴随紧密关系的某个集合SomeCollection,因此没有必要在属性级别上将它们关联起来。建立事件监听器和同步数据所需的工作只在一个地方。

// some controller (app main)

var model = new SomeSingletonModel();
var collection = new SomeSingletonCollection();

var view = new SomeView({
  model: model,
  collection: collection
});

映射到SomeSingletonModel的资源可能会传送一个数组。

使用普通数组作为模型属性(model.get("name")是什么)使用集合有什么好处?同步和更改事件。两者都可能只在View更新Collection的模型时才需要。当View仅渲染时,Collection在许多情况下不会提供任何好处。

如果需要更新该阵列的数据,由于Backbone的同步机制,使用Collection可能是正确的选择。

但是如何保持集合与模型同步(你问)?

您的控制器需要收听模型并更新syncreset上的集合:

model.on("sync reset", function() {
  // "priceCollection" is a model attribute 
  collection.reset(model.get("priceCollection"));

  // optionally unset "priceCollection" on the model
  this.unset("priceCollection", { silent: true });
});

这将初始化集合。

对Collection的模型的任何更改都只是Collection或Model的同步机制的一部分。

答案 1 :(得分:1)

前言

另见my other answer,当模型是单身时,这可能是更好的选择。

请注意关于Backbone在该答案上对API设计的假设的第一个声明。

使用模型和集合

之间的耦合的提案

注意:如有必要,在所有这些实现中,集合的url或模型(集合的模型)url / rootUrl可能会在sync上得到(重新)定义控制同步。

更新更改/同步/重置

的内部参考

此实现删除模型属性并使用其数据更新对象属性。

对象属性是一个仅在模型更改时重置但未重新创建的Collection实例。

var CustomModel = Backbone.Model.extend({
  defaults: {
    // defaults go here - "children" may be contained here
  },

  // implement constructor to act before the parent constructor is able to
  // call set() (L402 in v1.3.0) with the initial values
  // See https://github.com/jashkenas/backbone/blob/1.3.0/backbone.js#L402
  constructor: function() {
    // create children collection as object attribute - replaces model attr.
    this.children = new Backbone.Collection();
    // listen to changing events to catch away that attribute an update the
    // object attribute
    this.listenTo(this, "change:children sync reset", this.onChangeColl);

    // apply original constructor
    Backbone.Model.apply(this, arguments);
  },

  onChangeColl: function() {
    // check for presence since syncing will trigger "set" and then "sync",
    // the latter would then empty the collection again after it has been updated
    if (this.has("children")) {
      // update "children" on syncing/resetting - this will trigger "reset"
      this.children.reset(this.get("children"));

      // remove implicitly created model attribute
      // use silent to prevent endless loop due to change upon change event
      this.unset("children", { silent: true });
    }
  }
});

在小提琴或控制台中测试时的示例用法:

var c = new CustomModel({ a: 1, children: [{ x: 1 }, { x: 5 }] });
c.set({a: 8, children: [{ x: 50 }, { x: 89 }]});
c.url = "/dummy"
// replace sync() only for fetch() demo - the implementation does what sync() would do on success
c.sync = function(method, coll, opts){  if (method == "read") { opts.success({ a: 100, children: [{ x: 42 }, { x: 47 }] }); } }
c.fetch();
  • 监听集合事件更容易实现,因为模型生命周期中有一个实例
反对
  • 代码更复杂
  • 收集数据不是没有进一步实施的部分同步

替换更改/同步/重置

此实现拦截模型属性更改,并使用已使用原始数据初始化(重置)的Collection实例替换其数据。

var CustomModel = Backbone.Model.extend({
  defaults: {
    // this is optional
    children: new Backbone.Collection()
  },

  initialize: function() {
    // listen to model attribute changing events to swap the raw data with a
    // collection instance
    this.listenTo(this, "change:children sync reset", this.onChangeColl);
  },

  onChangeColl: function() {
    if (this.has("children")) {
      // use silent to prevent endless loop due to change upon change event
      this.set("children", new Backbone.Collection(this.get("children")), { silent: true });
    }
  }
});

在小提琴或控制台中测试时的示例用法:

var c = new CustomModel({ a: 1, children: [{ x: 1 }, { x: 5 }] });
c.set({ a: 8, children: [{ x: 50 }, { x: 89 }] });
c.url = "/dummy";
// replace sync() only for fetch() demo - the implementation does what sync() would do on success
c.sync = function(method, coll, opts){  if (method == "read") { opts.success({ a: 100, children: [{ x: 42 }, { x: 47 }] }); } }
c.fetch();
  • 直截了当的实施
反对
  • 同步中包含的数据,不包括它需要花费更多精力
  • 听取Collection不切实际:因为所有消费者都需要解绑/绑定

注意:根据您的要求和API设计,您可能不希望children自动同步到服务器。在这种情况下,该解决方案是有限的您可以覆盖模型的toJSON(),但这可能会限制其对应用程序其他部分的使用(例如将数据提供到视图中)。

反向关系:集合有一个模型

也许您的主要数据实际上是收藏品。因此,使用其他数据来装饰集合是另一种方法。此实现提供了Collection中的一个模型,该模型将在收集同步时更新。

此实现最适合于获取集合数据以及属性(例如,使用目录本身的属性获取目录内容)。

var CustomCollection = Backbone.Collection.extend({
   initialize: function() {
      // maintain decorative attributes of this collection
      this.attrs = new Backbone.Model();
   },

   parse: function(data, opts) {
      // remove "children" before setting the remainder to the Model
      this.attrs.set(_.omit(data, "children"));
      // return the collection content only
      return data.children;
   }
});

在小提琴或控制台中测试时的示例用法:

var c = new CustomCollection({ a: 1, b: 2, children: [{ x: 2 }, { x: 3 }] }, { parse: true });
c.reset({ a: 9, b: 11, children: [{ x: 5 }, { x: 10 }] } , { parse: true });
// replace sync() only for fetch() demo - the implementation does what sync() would do on success
c.sync = function(method, coll, opts){ if (method == "read") { opts.success({ a: 100, b: 124, children: [{ x: 42 }, { x: 47 }] }) } }
c.fetch();
  • 直接实施
  • 委派模型事件更容易
反对
  • 收集数据不是没有进一步实施的部分同步
  • 要求parse()实施哪个 - 反过来要求parse: true始终传递给reset()set()和 - 要求parse()以集合作为范围(this)进行调用(通过使用parse initialize定义this,可以避免这种情况。绑定()')