web-dev-qa-db-ja.com

複数のモデルサブクラスのBackbone.jsコレクション

私は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"属性に基づいて)?

63
Tricote

確かにあります。

コレクションで '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'

これの利点は、コレクションが、「フェッチ」以外の操作のためにログブックの正しいサブクラスを「キャスト」する方法を知るようになることです。

81
satchmorun

はい。コレクションの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":
               ...
          }
      }
    }
 });

お役に立てれば。

11
idbentley

バックボーン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
pinge

parseはそれ自体で機能するか、またはsubmodelTypes機能を使用できます Backbone-Relational

1
philfreo

多分それはevalを使うのは悪いことですが、これはずっとRubyスタイルの方法です(coffeescript):

  parse: (resp)->
    _(resp).map (attrs) ->
      eval("new App.Models.#{attrs.type}(attrs)")

したがって、多くのスイッチ/ケースを記述する必要はなく、JSONにtype属性を設定するだけです。 Rails + citierまたは他のマルチテーブル継承ソリューションで非常にうまく機能します。ケースに追加せずに新しい子孫を追加できます。

また、モデルクラスに応じて多くのスイッチ/ケースが必要な他の場所で、このような構造を使用できます。

0