позвоночник.коллекция 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 ответов:
есть на самом деле.
когда вы вызываете "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 или другим решением для наследования нескольких таблиц. Вы можете добавить новых потомков, не добавляя их в свои дела.
и вы можете использовать такие конструкции в других местах, где вам нужно много переключателей/случаев в зависимости от вашего класса модели.