10

我的web应用程序有一个复合结构,即每个类别集合可以包含单个项目和其他类别的混合作为其行/节点/子项(此处不确定正确的术语)。实际上,它比这更简单一些,因为每个集合都由一个模型Category来表示,所以基本上每个Category集合都具有Item模型和Category模型作为子模型。使用MVC/Backbone.js实现复合模式

一般来说,这是使用MVC实现这种结构的一种可取的方式吗?更具体地说,在Backbone.js中,一个集合有可能拥有一个模型工厂(取决于json并根据json的结构计算生成哪个模型)而不是静态模型属性?

+0

总之,我会说,“我不明白为什么不”,但我很好奇别人会说什么。 – JayC 2012-01-19 19:17:21

回答

1

是的,你可以。我以前做过。我觉得这个链接可以帮助你:http://documentcloud.github.com/backbone/#Collection-model

下面是我用我的项目的主脚本之一:https://gist.github.com/b65893e0c2e3c46d3dc1

+0

我真的无法弄清楚你在那个要点上做了什么。我只能看到一种类型的模型,TransactionDetail被添加到集合中,我希望自由添加两种类型。自从发布这个问题以来,我开始编辑Backbone以尝试获取我需要的内容,但是它为内存集合提供了内置的唯一ID规则,所以我认为我需要使用model type和id生成的id创建一些桥接模型。但我仍然有兴趣看看有没有更好的方法 – wheresrhys 2012-01-19 20:21:41

+0

我明白了。那么,我希望你能找到像你需要的方式去做。最好的运气。 – MauroPorrasP 2012-01-20 03:00:23

+0

他是对的,你可以给出一个工厂函数作为你的集合类的模型属性... http://documentcloud.github.com/backbone/#Collection-model – inf3rno 2013-04-23 21:41:42

5

我假设你接收JSON类别/项目列表,看起来像这...

{ 
    'id': 1, 
    'name': 'My 1st Category', 
    'children': [ 
     { 
      'id': 2, 
      'name': 'My 2nd Category', 
      'children': [] 
     }, 
     { 
      'id': 1, 
      'name': 'An Item', 
      'price': 109.99 
     } 
    ] 
} 

Backbone.js的没有任何关系了,它支持一个集合多种型号的方块,但它也不会对你把一个集合中的类型的模型任何限制。

在集合定义中指定模型类型只做一件事,它让Backbone知道如果将原始JSON传递给集合而不是Backbone.Model对象时要创建的模型类型。如果您将Item模型添加到已包含几个Category模型的集合中,Backbone将不会将其弹出到模型列表中;它不做任何类型检查。因此,考虑到这一点,除了传递原始JSON之外,您几乎可以使用集合提供的所有内容;你需要自己处理。所以你的选择是事先建立你的模型,使它们成为Backbone.Model对象,或者创建一些可以为你解析的东西。

对于第二个选项,解析器,我建议将一个特殊的变量传递给包含原始JSON的集合,然后在你的initialize函数中处理它。这里有一个例子:

var CategoryCollection = Backbone.Collection.extend({ 
    initialize: function(m, models) { 
     _.each(models, function(model) { 
      var modelObject = null; 
      if (model.price !== undefined) { 
       modelObject = new Item(model); 
      } else { 
       modelObject = new Category(model); 
      } 

      this.add(modelObject); 
     }, this); 
    } 
}); 

所以这是一个有点哈克,但你确定模型的基础上,类型,如果有(在我的例子price)的特定字段,创建模型对象,然后添加到采集。

你会然后调用它是这样的:你有

var myCollection = new CategoryCollection([], myJSON); 

通知传递一个空数组作为第一个参数,因为这就是你怎么正常通过一组模型到集合中。

使用集合时上以后,就可以判断,如果你有ItemCategory处理使用简单instanceof检查:

_.each(myCollection.models, function(model) { 
    if (model instanceof Item) { 
     console.log("It's an Item! Price: ", model.get("price")); 
    } else { 
     console.log("It's a Category!"); 
    } 
}); 
+0

整洁的想法,使用选项参数走私生数据。 – wheresrhys 2012-01-20 16:59:30

+0

哈哈谢谢。我认为这比黑客攻击Backbone.js源代码更容易。我想要清理它,你总是可以创建一个包装类,只传递你的JSON,然后创建并返回你的集合。 – 2012-01-20 17:10:49

+0

是的,我想劫持/包装一些其他收集方法,主要是因为内部Backbone.Model期望id是唯一的,例如我需要适应id,因此它或者接受一个键值对{modelType:id} ,或者减少到格式化的字符串。 – wheresrhys 2012-01-20 19:42:12

1

这是相当简单的通过覆盖内置的公共方法parse取得toJSON在检索和保存模型数据时由主干在内部使用。

首先,当您从数据库中检索模型时,您应该覆盖模型的parse方法,以创建表示示例中给定项目的模型。

然后保存toJSON方法用于将数据序列化回服务器可以理解的内容 - 您只需在每个项目的Model上调用toJSON方法即可将其序列化为后端将识别的格式。如果您查看Backbone.sync的代码,您将看到如果没有传递自定义数据,则模型总是使用toJSON进行序列化。

让我知道你是否需要更详细的信息,但我相信你应该能够从这里捡起它!

+0

我认为这可能构成解决方案的一部分,但也有其他方法可能需要调整,例如Backbone.Collection._prepareModel(对于我的应用程序,我可以确保我永远不会将原始json传递给Collection.add(调用。 prepareModel内部),但它会很高兴有一个解决方案,可以处理使用Collection.add为jsons数组 – wheresrhys 2012-01-26 10:13:01

+0

我相信覆盖模型的'set'方法来解析属性/通过JSON可以做诡计 - 它被称为当使用'new Model({...})'和使用'collection.add([{...},...])'' – 2012-01-26 13:52:00

+1

ps时,初始化模型以设置传递的属性默认情况下会扩展。尽管骨干0.9的快速接近发布,我们已经学会了艰难的方式,修改私有API方法可能会对你产生适得其反的作用:)这就是为什么我试图远离搞乱_prepareModel它不应该消失的原因 - 但是例如'_add'和'_remove'现在已经在主干 – 2012-01-26 13:54:20

1

是的,您可以使用工厂方法模式在骨干的集合中创建模型。假设在@kpeel's answer建议的数据结构,你可以定义

// Factory method 
var CategoryOrItemFactory = function(data, options) { 
    if (data.children) { 
     return new Category(data, options); 
    } else { 
     return new Item(data, options); 
    } 
}; 

// Model definitions 
var Item = Backbone.Model.extend(); 

var Category = Backbone.Model.extend({ 
    initialize: function() { 
     this.children = new Children(this.get("children")); 
    } 
}); 

var Children = Backbone.Collection.extend({ 
    model: CategoryOrItemFactory 
}); 

然后,您可以创建你的根项目(类别),并已经创建了完整的数据结构:

// create the root item 
var rootItem = new Category(rawData); 

访问类别的儿童及其children财产,例如

rootItem.children.get(2).get("name"); 

这里是一个jsFiddle with the above code玩。

+0

的主分支中消失了。但是仍然存在与数据库同步和Backbone.Collection对独特id的期望(例如,get无法正常工作http: //jsfiddle.net/Z7Dbr/ - 它返回第二个匹配结果并忽略第一个)。我注意到在你的例子中,Category扩展了模型而不是Collection,我也不确定我对此有多舒服,尽管为了避免对backbone.js进行广泛的重构,也许我必须创建一些混合类作为一个财产集合,而不是从集合继承 – wheresrhys 2012-01-26 11:34:32

+0

好吧,我试图回答你的问题:是否有可能建立模型工厂(是)。您的类别必须具有属性(例如ID),因此它们需要从模型派生(例如,集合的“get”完成与模型的方法不同的其他功能)。关于ID:首先如何识别数据条目?我想应该可以很容易地创建一个有意义和唯一的标识符? – 2012-01-26 13:49:19

+0

至于同步:应该可以为Items/Categories和Children设置正确的url值。你如何同步你的数据?你能告诉我们一些真实的数据吗? – 2012-01-26 13:57:29