-->

Backbone Collection of polymorphic Models

2019-04-22 03:57发布

问题:

I have a collection of Animals.

App.Collections.Animals extends Backbone.Collection
  model: App.Animal
  url: '/animals/' #returns json

And these animal classes:

App.Models.Animal extends Backbone.Model

App.Models.Monkey extends App.Models.Animal
  defaults:{type:'Monkey'}

App.Models.Cat extends App.Models.Animal
  defaults:{type:'Cat'}

App.Models.Dog extends App.Models.Animal
  defaults:{type:'Dog'}

When collection is filled with JSON (records contain the type attribute) I want models to be instantiated as sub-classed models (Monkey,Cat,Dog) and not as Animal. How can you achieve this?

回答1:

From Backbone documentation:

A collection can also contain polymorphic models by overriding this property with a function that returns a model.

var Library = Backbone.Collection.extend({

  model: function(attrs, options) {
    if (condition) {
      return new PublicDocument(attrs, options);
    } else {
      return new PrivateDocument(attrs, options);
    }
  }

});


回答2:

The solution is straightforward (pardon the JS, I don't know CoffeeScript):

var SmartZoo = Backbone.Collection.extend({
    model: function (attrs, options) {
        // This code assumes that the object looks something like '{ type: "Cat", ... }'.
        switch (attrs.type) {
            case 'Cat':
                return new Cat(attrs, options);
            case 'Dog':
                return new Dog(attrs, options);
            default: // Unknown subclass
                return new Animal(attrs, options);
        }
    }
});

You have to:

  1. Include an attribute in your model from which you can infer the type of Backbone model to create. In this example, my objects contain an attribute called "type" whose value is the full name of the Backbone type that represents it. Be sure to set it in the defaults or initialize of your Model so that you can also add real model instances to the collection.
  2. Define the models property of your collection as a function. The first parameter to this function will be the raw JS object (if that's what you passed in), or the attributes object of a Backbone model. Either way, you can access your type field as a property of this object.
  3. Execute your logic to infer the proper model from your type field.
  4. Return an instance of the correct model from the models function.

Here is a JSFiddle that shows this polymorphic collection in action: http://jsfiddle.net/FiddlerOnTheTarmac/uR2Sa/



回答3:

Override backbone collection's _prepareModel. The collection new uses subclasses when defined otherwise uses the default model.

class App.Collections.Animals extends Backbone.Collection

 model: App.Models.Animal

 _prepareModel: (attrs, options) ->
   if attrs instanceof Backbone.Model
   attrs.collection = @
   return attrs

   options || (options = {})
   options.collection = @
   model_class = APP.Models[attrs.ntype] or this.model
   model = new model_class(attrs, options)
   if (!model._validate(attrs, options))
     false
   else
     model