我有一个REST Json API,它返回一个列表“logbooks”。有许多类型的日志可以实现不同但相似的行为。数据库层上的服务器端实现是一种单表继承,因此日志的每个JSON表示都包含其“类型”:
[
{"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”属性)?
答案 0 :(得分:81)
当您在集合上调用'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
编辑:哇,idbentley在我之前到达那里。唯一的区别是他使用'each'而我使用'map'。两者都有效,但不同。
使用'each'有效地打破'fetch'调用开始的链(通过返回'undefined' - 随后调用'reset'(或'add')因此将不执行任何操作)并完成所有处理在解析函数中。
使用'map'只需将属性列表转换为模型列表,并将其传递回已经运动的链。
不同的笔触。
再次编辑:刚刚意识到还有另一种方法可以做到这一点:
集合上的'model'属性只有这样,如果集合在'add','create'或'reset'中传递属性,集合就知道如何制作新模型。所以你可以这样做:
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'
这样做的好处是,该集合现在将知道如何为“fetch”以外的操作“强制转换”Logbook的正确子类。
答案 1 :(得分:11)
是。您可以覆盖集合上的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":
...
}
}
}
});
希望这有帮助。
答案 2 :(得分:3)
从骨干0.9.1开始,我开始使用esa-matti suuronen的拉动请求中描述的方法:
https://github.com/documentcloud/backbone/pull/1148
应用补丁后,您的收藏将是这样的:
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)
答案 3 :(得分:1)
parse
可以单独使用,也可以使用submodelTypes的 Backbone-Relational 功能。
答案 4 :(得分:0)
使用eval也许不好,但这更像是红宝石风格(coffeescript):
parse: (resp)->
_(resp).map (attrs) ->
eval("new App.Models.#{attrs.type}(attrs)")
因此您不需要编写大量的开关/案例,只需在JSON中设置type属性即可。它对rails + citier或其他多重继承解决方案非常有用。您可以添加新的后代,而无需将其添加到您的案例中。
您可以在其他需要大量开关/案例的地方使用此类构造,具体取决于您的模型类。