J'ai un REST API Json qui renvoie une liste de "journaux de bord". Il existe de nombreux types de journaux de bord qui implémentent un comportement différent mais similaire. L'implémentation côté serveur de cela sur la couche base de données est une sorte de Héritage de table unique, donc chaque représentation JSON d'un journal de bord 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 répliquer ce modèle de serveur côté client, j'ai donc une classe de base Logbook
et plusieurs sous-classes de journal de bord:
class Logbook extends Backbone.Model
class UmlLogbook extends Logbook
class PlaneLogbook extends Logbook
...
Ma Backbone.Collection
est un ensemble de Logbook
modèles 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 de bord, existe-t-il un moyen de convertir chaque Logbook
en sa sous-classe correspondante (basée sur l'attribut "type" JSON)?
Il y en a en effet.
Lorsque vous appelez "récupérer" sur une collection, il transmet la réponse via Backbone.Collection.parse avant de l'ajouter à la collection.
L'implémentation par défaut de 'parse' transmet simplement la réponse telle quelle, mais vous pouvez la remplacer pour renvoyer une liste de modèles à ajouter à 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 a utilisé "chacun" et j'ai utilisé "carte". Les deux fonctionneront, mais différemment.
L'utilisation de 'each' rompt effectivement la chaîne que l'appel 'fetch' a commencée (en renvoyant 'undefined' - l'appel suivant à 'reset' (ou 'add') ne fera donc rien) et fait tout le traitement directement dans l'analyse fonction.
L'utilisation de "map" transforme simplement la liste d'attributs en une liste de modèles et la retransmet à la chaîne déjà en mouvement.
Différents coups.
MODIFIER ENCORE: vient de réaliser qu'il existe également une autre façon de procéder:
L'attribut "modèle" sur une collection n'est là que pour que la collection sache comment créer un nouveau modèle si ses attributs sont passés dans "ajouter", "créer" ou "réinitialiser". 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 "transtyper" la bonne sous-classe de Logbook pour des opérations autres que "extraire".
Oui. Vous pouvez remplacer la fonction parse
sur la collection (je vais utiliser javascript au lieu de coffeescript, car c'est ce que je sais, mais le mappage devrait ê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.
depuis le backbone 0.9.1, j'ai commencé à utiliser la méthode décrite dans la pull-request d'esa-matti suuronen:
https://github.com/documentcloud/backbone/pull/1148
après avoir appliqué le patch, votre collection ressemblerait à 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 fonction submodelTypes de Backbone -Relationnel .
Peut-être que c'est mauvais d'utiliser eval, mais c'est beaucoup plus de façon Ruby (coffeescript):
parse: (resp)->
_(resp).map (attrs) ->
eval("new App.Models.#{attrs.type}(attrs)")
Vous n'avez donc pas besoin d'écrire beaucoup de commutateurs/cas, il suffit de définir l'attribut type dans votre JSON. Cela fonctionne très bien avec Rails + citier ou une autre solution d'héritage multitable. Vous pouvez ajouter de nouveaux descendants sans les ajouter à vos cas.
Et vous pouvez utiliser de telles constructions dans d'autres endroits où vous avez besoin de beaucoup de commutateurs/boîtiers en fonction de votre classe de modèle.