A Backbone.js Collection of multiple Model subclasses

后端 未结 5 1572
没有蜡笔的小新
没有蜡笔的小新 2020-12-07 12:13

I have a REST Json API that returns a list \"logbooks\". There are many types of logbooks that implement different but similar behavior. The server side implementation of th

相关标签:
5条回答
  • 2020-12-07 12:39

    Maybe it's bad to use eval, but this is much more ruby-style way (coffeescript):

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

    So you don't need to write a lot of switch/cases, just set type attribute in your JSON. It works very good with rails+citier or other multitable inheritance solution. You can add new descendants without adding them to your cases.

    And you can use such constructions in other places where you need a lot of switch/cases depending on your model class.

    0 讨论(0)
  • 2020-12-07 12:56

    Yes. You can override the parse function on the collection (I'm gonna use javascript instead of coffeescript, because it's what I know, but the mapping should be easy):

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

    Hope this helps.

    0 讨论(0)
  • 2020-12-07 12:59

    as of backbone 0.9.1, i've started using the method described in esa-matti suuronen's pull-request:

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

    after applying the patch, your collection would be something like this:

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

    i believe this would fit since you're using STI (all models have unique ids)

    0 讨论(0)
  • 2020-12-07 12:59

    parse can work on its own, or you could use the submodelTypes feature of Backbone-Relational.

    0 讨论(0)
  • 2020-12-07 13:01

    There is indeed.

    When you call 'fetch' on a collection, it passes the response through Backbone.Collection.parse before adding it to the collection.

    The default implementation of 'parse' just passes the response through, as is, but you can override it to return a list of models to be added to the 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 got there before me. the only difference being he used 'each' and I used 'map'. Both will work, but differently.

    Using 'each' effectively breaks the chain that the 'fetch' call started (by returning 'undefined' - the subsequent call to 'reset' (or 'add') therefore will do nothing) and does all the processing right there in the parse function.

    Using 'map' just transforms the list of attributes into a list of models and passes it back to the chain already in motion.

    Different strokes.

    EDIT AGAIN: just realized there's also another way to do this:

    The 'model' attribute on a collection is there only so the collection knows how to make a new model if it's passed attributes in 'add', 'create' or 'reset'. So you could do something like:

    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'
    

    The advantage of this is that the collection will now know how to 'cast' the right subclass of Logbook for operations other than 'fetch'.

    0 讨论(0)
提交回复
热议问题