позвоночник.коллекция 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 набор Logbook модели, которые я использую для запроса JSON API:

class LogbookCollection extends Backbone.Collection
  model: Logbook
  url: "/api/logbooks"

когда я получаю коллекцию журналов, есть ли способ бросить каждый Logbook к соответствующему подклассу (на основе атрибута JSON "type")?

5 62

5 ответов:

есть на самом деле.

когда вы вызываете "fetch" в коллекции, он передает ответ через магистраль.Коллекция.разбор перед добавлением его в коллекцию.

реализация по умолчанию '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

EDIT: вау, идбентли добрался туда раньше меня. единственная разница в том, что он использовал "каждый", а я использовал "карту". Оба будут работают, но по-другому.

использование 'each' эффективно разрывает цепочку, которую начал вызов ' fetch '(возвращая' undefined '- последующий вызов' reset '(или' add') поэтому ничего не будет делать) и выполняет всю обработку прямо в функции parse.

С помощью' 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'

преимущество этого заключается в том, что коллекция теперь будет знать, как "бросить" правильный подкласс журнала для операций, отличных от "выборки".

да. Вы можете переопределить 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":
               ...
          }
      }
    }
 });

надеюсь, что это помогает.

начиная с backbone 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 (все модели имеют уникальные идентификаторы)

parse может работать самостоятельно, или вы можете использовать submodelTypes особенность Backbone-Relational.

может быть, это плохо использовать eval, но это гораздо более рубиновый способ (coffeescript):

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

поэтому вам не нужно писать много переключателей/случаев, просто установите атрибут type в своем JSON. Он очень хорошо работает с rails+citier или другим решением для наследования нескольких таблиц. Вы можете добавить новых потомков, не добавляя их в свои дела.

и вы можете использовать такие конструкции в других местах, где вам нужно много переключателей/случаев в зависимости от вашего класса модели.