PaulUithol / Backbone-relational

Get and set relations (one-to-one, one-to-many, many-to-one) for Backbone models
http://backbonerelational.org
MIT License
2.34k stars 330 forks source link

subModelTypes as class references instead of string class names #508

Open ghost opened 9 years ago

ghost commented 9 years ago

Why subModelTypes classes can be only strings? Is it possible/correct to have subModelTypes class references?

For example this code

Mammal = Animal.extend({
    subModelTypes: {
        'primate': 'Primate',
        'carnivore': 'Carnivore'
    }
});

Primate = Mammal.extend();
Carnivore = Mammal.extend();

MammalCollection = AnimalCollection.extend({
    model: Mammal
});

to work the same as this

Mammal = Animal.extend({
    subModelTypes: {}
});

Primate = Mammal.extend();
Mammal.prototype.subModelTypes.primate = Primate;

Carnivore = Mammal.extend();
Mammal.prototype.subModelTypes.carnivore = Carnivore;

MammalCollection = AnimalCollection.extend({
    model: Mammal
});

In our application, we create a base class, send it in event, anyone can listen the event and register its subClass

// file: events.js
(function(){
  window.events = {};
  _.extend(window.events, Backbone.Events);
})();

// file: primate.js
(function(){
  events.on("register:mammals", function(Mammal) {
    var Primate = Mammal.extend({defaults: {title: 'Primate'}});
    Mammal.prototype.subModelTypes.primate = Primate;
  });
})();

// file: carnivore.js
(function(){
  events.on("register:mammals", function(Mammal) {
    var Carnivore = Mammal.extend({defaults: {title: 'Carnivore'}});
    Mammal.prototype.subModelTypes.carnivore = Carnivore;
  });
})();

// file: mammals.js
(function(){
  var Mammal = Backbone.RelationalModel.extend({
    subModelTypes: {}
  });

  events.trigger("register:mammals", Mammal);

  var MammalCollection = Backbone.Collection.extend({
    model: Mammal
  });

  var mammals = new MammalCollection;
  mammals.reset([{type: 'primate'}, {type: 'carnivore'}]);

  console.log(mammals.toJSON());
  console.log(
    mammals.at(0).get('type'),
    mammals.at(0) instanceof Mammal.prototype.subModelTypes[mammals.at(0).get('type')], 
    mammals.at(0).get('title')
  );
  console.log(
    mammals.at(1).get('type'),
    mammals.at(1) instanceof Mammal.prototype.subModelTypes[mammals.at(1).get('type')],
    mammals.at(1).get('title')
  );
})();

If I change the getObjectByName method from

getObjectByName: function( name ) {
    var parts = name.split( '.' ),
        type = null;

    ...
}

to

getObjectByName: function( name ) {
    if (name.prototype instanceof Backbone.RelationalModel) {
        return name;
    }

    var parts = name.split( '.' ),
        type = null;

    ...
}

then the example above seems to work. But I am not sure if it's the right way to do this. Also I don't know how this will affect the store.


Because it is not possible to have subModelTypes as class references, we did this:

Then we faced the problem that MammalCollection.model is replaced by Backbone-relational. More details in #472

ghost commented 9 years ago

I created a pull request with some tests in case you will accept this feature. If this is not the way it should be solved, fell free to close that pull request

hippich commented 9 years ago

This looks like a right change.