Une collection Backbone.js de plusieurs sous-classes Model

J’ai une API REST Json qui renvoie une liste “journaux de bord”. Il existe de nombreux types de journaux de bord qui implémentent des comportements différents mais similaires. L’implémentation côté serveur de la couche Database est une sorte d’inheritance de table unique, de sorte que chaque représentation JSON d’un journal contient son “type”:

[ {"type": "ULM", "name": "My uml logbook", ... , specific_uml_logbook_attr: ...}, {"type": "Plane", "name": "My plane logbook", ... , specific_plane_logbook_attr: ...} ] 

Je voudrais reproduire ce modèle de serveur du côté client, donc j’ai une classe de Logbook base et plusieurs sous-classes de journal:

 class Logbook extends Backbone.Model class UmlLogbook extends Logbook class PlaneLogbook extends Logbook ... 

My Backbone.Collection est un ensemble de modèles de Logbook que j’utilise pour interroger l’API JSON:

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

Lorsque je récupère la collection de journaux, existe-t-il un moyen de convertir chaque Logbook en sous-classe correspondante (en fonction de l’atsortingbut “type” JSON)?

Il y a bien

Lorsque vous appelez ‘récupérer’ sur une collection, elle transmet la réponse via Backbone.Collection.parse avant de l’append à la collection.

L’implémentation par défaut de ‘parse’ transmet simplement la réponse, mais vous pouvez la remplacer pour renvoyer une liste de modèles à append à la collection:

 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: Whoa, idbentley est arrivé avant moi. la seule différence étant qu’il utilisait «chacun» et que j’utilisais «carte». Les deux fonctionneront, mais différemment.

Utiliser ‘each’ casse efficacement la chaîne que l’appel de ‘fetch’ a lancée (en renvoyant ‘undefined’ – l’appel suivant à ‘reset’ (ou ‘add’) ne fera donc rien) et effectuera tout le traitement dans l’parsing fonction.

Utiliser ‘map’ transforme simplement la liste des atsortingbuts en une liste de modèles et la renvoie à la chaîne déjà en mouvement.

Différents coups

EDIT AGAIN: vient de réaliser qu’il existe également une autre façon de procéder:

L’atsortingbut ‘model’ sur une collection n’est là que pour que la collection sache comment créer un nouveau modèle si des atsortingbuts ont été transmis dans ‘add’, ‘create’ ou ‘reset’. Vous pourriez donc faire quelque chose comme:

 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' 

L’avantage de ceci est que la collection saura désormais comment «lancer» la bonne sous-classe de Logbook pour des opérations autres que «fetch».

Oui. Vous pouvez remplacer la fonction d’ parse sur la collection (je vais utiliser javascript au lieu de coffeescript, car c’est ce que je sais, mais le mappage doit être facile):

 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": ... } } } }); 

J’espère que cela t’aides.

à partir du backbone 0.9.1, j’ai commencé à utiliser la méthode décrite dans la requête pull d’esa-matti suuronen:

https://github.com/documentcloud/backbone/pull/1148

Après avoir appliqué le patch, votre collection serait comme ceci:

 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); } } }); 

Je crois que cela conviendrait puisque vous utilisez STI (tous les modèles ont des identifiants uniques)

parse peut fonctionner seul ou vous pouvez utiliser la fonctionnalité submodelTypes de Backbone-Relational .

Peut-être que c’est mauvais d’utiliser eval, mais c’est beaucoup plus le style ruby ​​(coffeescript):

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

Vous n’avez donc pas besoin d’écrire beaucoup de switch / cases, définissez simplement l’atsortingbut type dans votre JSON. Cela fonctionne très bien avec rails + citier ou autre solution d’inheritance multitable. Vous pouvez append de nouveaux descendants sans les append à vos cas.

Et vous pouvez utiliser de telles constructions dans d’autres endroits où vous avez besoin de beaucoup d’interrupteurs / étuis en fonction de votre classe de modèle.