2013-08-06 47 views
2

假設我正在處理一個返回JSON數據但具有複雜或可變結構的API。例如,一個字符串值屬性可能是一個普通的文字,或者可以用語言來標記:面向對象的模型和backbone.js

/* 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 pattern,這是有幫助的,因爲它更從具體JSON結構,更容易測試等脫鉤好,好。

我目前看不到解決方法是如何使用Backbone和Marionette中的這些值對象之一。具體來說,我想使用Foo對象作爲Backbone Model的基礎,並將其綁定到Marionette ItemView。然而,據我所看到的,在Model值是直接取自JSON結構 - 我看不到的方式來識別的對象是函數:

var modelFoo = new Backbone.Model(foo); 
> undefined 
modelFoo.get("label").constructor 
> function Function() { [native code] } 

所以我的問題是:什麼將Backbone Model的屬性與給定的JSON結構的特性(如複雜的API值)分離是一種好方法?可以評估對象,模型和視圖的好壞嗎?

編輯

讓我補充一個例子,因爲我認爲上述重點放在國際化問題的例子只是傳達我所關注的一部分。簡化一下,在我的領域,我擁有包括河流,湖泊和潮間帶的水體。水體與一個或多個採樣點相關聯,並且每個採樣點具有最新的樣本。這可能來自於數據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() %> 

一個我與木偶發現的主要問題是堅持上調用toJSON()模型傳遞給視圖,但也許一些計算出來的屬性建議有辦法解決這個問題。

+1

你可能想看看模型中'serializeData'的用法。您可以以任何您認爲適合該視圖的方式來操縱模型。 – kalley

+0

更新了我的回答,以便對您的編輯進行回覆 – Creynders

回答

1

的乾淨的解決方案IMO是把標籤訪問到模型中,而不是VO:

var FooModel = Backbone.Model.extend({ 
    getLabel : function(){ 
     return this.getLocalized("label"); 
    }, 
    getLocalized : function(key){ 
     //return correct value from "label" array 
    } 
}); 

,讓視圖使用FooModel#getLabel代替FooModel#get("label")

--edit 1

這個lib對你的用例來說似乎也很有趣:Backbone.Schema

它允許你正式取代e模型屬性的類型,還爲本地化字符串提供了一些語法糖,並允許您創建由其他屬性的值組成的動態屬性(稱爲「計算屬性」)。

--edit 2(響應於所編輯的問題)

IMO的VO從服務器返回應的模型內纏繞並且該模型被傳遞給圖。該模型實現latestClassification,而不是VO,這允許視圖直接在模型上調用該方法。

+0

Backbone.Schema看起來很有趣,感謝指針。我已經更新了這個問題,以澄清我在問題背後存在的其他一些擔憂。 –

1

一個簡單的方法來這個(可能是爲了簡單的爲您實現)將覆蓋模型的parse方法返回合適的屬性:關於從服務器數據如何

var modelFoo = Backbone.Model.extend({ 
    parse: function (json) { 
     var i18n = ... ; 
     if (i18n.prefLang && _.isArray(json.label)) { 
      // json.label = "complex structure" 
     } 
     return json; 
    } 
}); 

這樣,只有你的模型後顧之憂被格式化而不添加另一層抽象。

+0

所以對待就像一種正面模式。好的,我可以看到。就i18n標籤而言,丟棄非首選語言標籤意味着如果用戶在UI中切換語言,則必須要求服務器重新發送數據。但是,如果從服務器數據到演示文稿的映射是靜態的,這將是一種可能的策略。 –

+0

@IanDickinson你總是可以將原始標籤移動到另一個屬性。像'json.labels = json.label; json.label =「complex value」;'如果'json.label'不是一個數組,你可以使用'json.labels = [json.label]'將它添加到json中併爲它創建一個一致的API。 – kalley

+0

感謝您的建議。我編輯了這個問題以擴大我的潛在關注點。 –