假设我正在使用一个返回 JSON 数据但具有复杂或可变结构的 API。例如,一个字符串值的属性可以是一个普通的文字,或者可以用一种语言标记:
/* first pattern */
{ "id": 1,
"label": "a foo"
}
/* second pattern */
{ "id": 2,
"label": [ {"value": "a foo", "lang": "en"},
{"value": "un foo", "lang": "fr"}]
}
在我的客户端代码中,我不想让视图代码担心标签是否可用于多种语言,以及选择哪一种等。或者我可能出于其他原因想要隐藏详细的 JSON 结构。因此,我可能会使用合适的 API 将 JSON 值包装在一个对象中:
/** Value object for foo instances sent from server */
var Foo = function( json ) {
this.json = json;
};
/** Return a suitable label for this foo object */
Foo.prototype.label = function() {
var i18n = ... ;
if (i18n.prefLang && _.isArray(this.json.label)) // ... etc etc
};
所以这都是非常正常的 value-object 模式,它很有帮助,因为它与特定的 JSON 结构更加解耦,更可测试等等。好的。
我目前看不到的方法是如何将这些值对象之一与 Backbone 和 Marionette 一起使用。具体来说,我想使用一个Foo
对象作为 Backbone 的基础Model
,并将其绑定到 Marionette ItemView
。但是,据我所见, a 中的值Model
直接取自 JSON 结构 - 我看不到识别对象是函数的方法:
var modelFoo = new Backbone.Model( foo );
> undefined
modelFoo.get( "label" ).constructor
> function Function() { [native code] }
所以我的问题是:将 Backbone 的属性与Model
给定 JSON 结构的细节(例如复杂的 API 值)分离的好方法是什么?值对象、模型和视图可以做得很好吗?
编辑
让我再举一个例子,因为我认为上面关注 i18n 问题的例子只表达了我的部分担忧。稍微简化一下,在我的领域中,我的水体包括河流、湖泊和潮间带。一个水体关联了一个或多个采样点,每个采样点都有一个最新的样本。这可能来自服务器上的数据 API,如下所示:
{"id": "GB12345678",
"centre": {"lat": 1.2345, "long": "-2.3456"},
"type": "river",
"samplingPoints": [{"id": "sp98765",
"latestSample": {"date": "20130807",
"classification": "normal"}
}]
}
因此,在我的视图代码中,我可以编写如下表达式:
<%= waterbody.samplingPoints[0].latestSample.classification %>
或者
<% if (waterbody.type === "river") { %>
但如果 API 格式发生变化,那将是可怕的,并且很容易被破坏。稍微好一点的是,我可以将这些操作抽象到模板辅助函数中,但仍然很难为它们编写测试。我想做的是有一个值对象类Waterbody
,这样我的视图代码就可以有类似的东西:
<%= waterbody.latestClassification() %>
我发现 Marionette 的主要问题之一是坚持调用toJSON()
传递给视图的模型,但也许一些计算属性建议有办法解决这个问题。