Kręgosłup.js zbiór wielu podklas modeli

Mam REST JSON API, które zwraca listę "dzienników". Istnieje wiele rodzajów dzienników, które implementują różne, ale podobne zachowanie. Po stronie serwera implementacja tego na warstwie bazy danych jest rodzajem dziedziczenia pojedynczej tabeli, więc każda reprezentacja JSON dziennika zawiera swój "typ":

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

Chciałbym replikować ten model serwera po stronie klienta, więc mam klasę bazową Logbook i wiele podklas dziennika:

class Logbook extends Backbone.Model

class UmlLogbook extends Logbook

class PlaneLogbook extends Logbook

...

Mój Backbone.Collection jest zbiorem Logbook modele, których używam do odpytywania interfejsu API JSON:

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

Kiedy pobieram kolekcję dziennika, czy istnieje sposób, aby rzucić każdą Logbook do odpowiadającej jej podklasy (na podstawie atrybutu JSON "type")?

Author: Prasanth A R, 2011-08-04

5 answers

W rzeczy samej.

Gdy wywołujesz 'fetch' na kolekcji, przekazuje ona odpowiedź przez kręgosłup.Kolekcja.analizuj przed dodaniem go do kolekcji.

Domyślna implementacja 'parse' po prostu przekazuje odpowiedź, tak jak jest, ale można ją zastąpić, aby zwrócić listę modeli do dodania do kolekcji:

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 dotarł tam przede mną. jedyną różnicą jest to, że on użył 'każdego', a ja 'mapy'. Oba będą działać, ale inaczej.

Użycie 'each' skutecznie łamie łańcuch, który rozpoczęło wywołanie' fetch' (zwracając 'undefined' - kolejne wywołanie 'reset' (lub' add'), więc nic nie da) i wykonuje całe przetwarzanie właśnie tam w funkcji parse.

Użycie ' map ' po prostu przekształca listę atrybutów w listę modeli i przekazuje ją z powrotem do łańcucha już w ruchu.

Różne uderzenia.

EDIT AGAIN: właśnie zdałem sobie sprawę, że jest jeszcze inny sposób na to:

Atrybut 'model' na kolekcji jest tam tylko dlatego, że kolekcja wie, jak utworzyć nowy model, jeśli przekazuje atrybuty w "Dodaj", "Utwórz" lub "reset". Więc możesz zrobić coś takiego:

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'

Zaletą tego jest to, że kolekcja będzie teraz wiedziała, jak "rzucić" właściwą podklasę dziennika do operacji innych niż "fetch".

 80
Author: satchmorun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-04 00:22:09

Tak. Możesz nadpisać funkcję parse Na kolekcji (użyję javascript zamiast coffeescript, bo to Wiem, ale mapowanie powinno być łatwe):

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":
               ...
          }
      }
    }
 });
Mam nadzieję, że to pomoże.
 11
Author: idbentley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-03 23:37:21

Od wersji 0.9.1 zacząłem używać metody opisanej w esa-matti suuronen ' s pull-request:

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

Po nałożeniu patcha, Twoja kolekcja będzie wyglądać mniej więcej tak:

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

});

Wierzę, że to pasuje, ponieważ używasz STI (wszystkie modele mają unikalne identyfikatory)

 3
Author: pinge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-31 16:32:46

parse może pracować samodzielnie, lub można użyć submodeltypy Funkcja Backbone-Relational .

 1
Author: philfreo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-04 01:49:51

Może źle jest używać evala, ale jest to znacznie bardziej styl ruby (coffeescript):

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

Więc nie musisz pisać wielu Switch/cases, po prostu ustaw atrybut type w swoim JSON. Działa bardzo dobrze z rails+citier lub innym wielozadaniowym rozwiązaniem dziedziczenia. Możesz dodawać nowych Potomków bez dodawania ich do swoich spraw.

I możesz używać takich konstrukcji w innych miejscach, gdzie potrzebujesz dużo przełączników / obudów w zależności od klasy modelu.

 0
Author: Alexander Ulitin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-01 19:29:18