Backbone.js多个模型子类的集合
我有一个REST Json API返回一个列表“日志”。 有许多types的日志实现不同但相似的行为。 数据库层的服务器端实现是一种单表inheritance,所以日志的每个JSON表示都包含它的“types”:
[ {"type": "ULM", "name": "My uml logbook", ... , specific_uml_logbook_attr: ...}, {"type": "Plane", "name": "My plane logbook", ... , specific_plane_logbook_attr: ...} ]
我想在客户端复制这个服务器模型,所以我有一个基本的Logbook
类和多个日志子类:
class Logbook extends Backbone.Model class UmlLogbook extends Logbook class PlaneLogbook extends Logbook ...
我的Backbone.Collection
是一组用于查询JSON API的Logbook
模型:
class LogbookCollection extends Backbone.Collection model: Logbook url: "/api/logbooks"
当我获取日志集合,是否有办法将每个Logbook
转换为其相应的子类(基于JSON“type”属性)?
确实有。
在集合上调用“fetch”时,在将其添加到集合之前,它会通过Backbone.Collection.parse传递响应。
“parse”的默认实现只是按照原样传递响应,但您可以覆盖它以返回要添加到集合的模型列表:
class Logbooks extends Backbone.Collection model: Logbook url: 'api/logbooks' parse: (resp, xhr) -> _(resp).map (attrs) -> switch attrs.type when 'UML' then new UmlLogbook attrs when 'Plane' then new PLaneLogbook attrs
编辑:哇,伊本普利在我之前到了那里。 唯一的区别是他使用“每个”,我用“地图”。 两者都将工作,但不同。
使用'each'有效地打破了'fetch'调用开始的链(通过返回'undefined' – 随后的调用'reset'(或'add')因此将什么都不做)并且在parsing中进行所有处理function。
使用'map'只是将属性列表转换成一个模型列表,并将其传递回已经运动的链。
不同的笔画。
再次编辑:刚才意识到还有另一种方法来做到这一点:
集合上的“模型”属性只有在集合知道如何在“添加”,“创build”或“重置”中传递属性时才能创build新模型。 所以你可以做这样的事情:
class Logbooks extends Backbone.Collection model: (attrs, options) -> switch attrs.type when 'UML' then new UmlLogbook attrs, options when 'Plane' then new PLaneLogbook attrs, options # should probably add an 'else' here so there's a default if, # say, no attrs are provided to a Logbooks.create call url: 'api/logbooks'
这样做的好处是,集合现在将知道如何将“日志”的正确子类“投”到“取”之外的操作。
是。 你可以重写集合上的parse
函数(我将使用JavaScript而不是coffeescript,因为这是我所知道的,但映射应该很容易):
LogbookCollection = Backbone.Collection.extend({ model: Logbook, url: "/api/logbooks", parse: function(response){ var self = this; _.each(response, function(logbook){ switch(logbook.type){ case "ULM": self.add(new UmlLogBook(logbook); break; case "Plane": ... } } } });
希望这可以帮助。
作为骨干0.9.1,我已经开始使用esa-matti suuronen的pull-request中描述的方法:
https://github.com/documentcloud/backbone/pull/1148
在应用补丁之后,你的collections将是这样的:
LogbookCollection = Backbone.Collection.extend({ model: Logbook, createModel: function (attrs, options) { if (attrs.type === "UML") { // i'am assuming ULM was a typo return new UmlLogbook(attrs, options); } else if (attrs.type === "Plane") { return new Plane(attrs, options); } else { return new Logbook(attrs, options); // or throw an error on an unrecognized type // throw new Error("Bad type: " + attrs.type); } } });
我相信这会适合你使用STI(所有型号都有独特的ID)
parse
可以独立工作,也可以使用Backbone-Relational的子模型types特性。
也许这是不好的使用eval,但这是更ruby式的方式(coffeescript):
parse: (resp)-> _(resp).map (attrs) -> eval("new App.Models.#{attrs.type}(attrs)")
所以你不需要写很多switch / case,只需在你的JSON中设置type属性即可。 它与rails + citier或其他多inheritance解决scheme非常好。 您可以添加新的后代,而无需将其添加到您的案例中。
而且你可以在其他地方使用这样的结构,在那里你需要很多的开关/例子,这取决于你的模型类。