私はRESTリスト「ログブック」を返すJson APIを持っています。異なるが類似の動作を実装するログブックには多くのタイプがあります。データベース層でのこれのサーバー側の実装は一種です。単一テーブルの継承。ログブックの各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"属性に基づいて)?
確かにあります。
コレクションで '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'を使用すると、 'fetch'呼び出しが開始したチェーンが効果的に解除され( 'undefined'が返されるため、その後の 'reset'(または 'add')の呼び出しでは何も行われません)、解析内のすべての処理が実行されます関数。
「マップ」を使用すると、属性のリストがモデルのリストに変換され、すでに動いているチェーンに戻されます。
異なるストローク。
再度編集:これを行う別の方法もあることに気づきました:
コレクションの「モデル」属性はそこにのみ存在するため、「追加」、「作成」、または「リセット」で属性が渡された場合、コレクションは新しいモデルを作成する方法を認識します。だからあなたは次のようなことをすることができます:
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
関数をオーバーライドできます(私が知っていることなので、coffeescriptではなくjavascriptを使用しますが、マッピングは簡単です):
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のプルリクエストで説明されている方法の使用を開始しました。
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があります)
parse
はそれ自体で機能するか、またはsubmodelTypes機能を使用できます Backbone-Relational 。
多分それはevalを使うのは悪いことですが、これはずっとRubyスタイルの方法です(coffeescript):
parse: (resp)->
_(resp).map (attrs) ->
eval("new App.Models.#{attrs.type}(attrs)")
したがって、多くのスイッチ/ケースを記述する必要はなく、JSONにtype属性を設定するだけです。 Rails + citierまたは他のマルチテーブル継承ソリューションで非常にうまく機能します。ケースに追加せずに新しい子孫を追加できます。
また、モデルクラスに応じて多くのスイッチ/ケースが必要な他の場所で、このような構造を使用できます。