Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow passing polymorphic models to collections #73

Merged
merged 2 commits into from
Oct 10, 2013

Conversation

dak
Copy link
Contributor

@dak dak commented Oct 6, 2013

Allow polymorphic models to be used in a collection. This can be done by simply passing a function to relatedModel that returns the function you would normally set to a Backbone collection's model attribute.

relatedModel: function (relation, attributes) {
    return function (attrs, options) {
        if (attrs.isBranch) {
            return new Tree(attrs);
        }

        return new Leaf(attrs);
    }
}

This is particularly useful when you're working with a tree with multiple models in it, as in the coffeescript example below:

class Tree extends Backbone.AssociatedModel
  relations: [{
    type: Backbone.Many
    key: 'tree'
    relatedModel: (relation, attributes) ->
      return (attrs, options) ->
        if attrs.isBranch
          return new Tree(attrs)

        return new Leaf(attrs)
  }]

@ghost ghost assigned dhruvaray Oct 8, 2013
@dhruvaray
Copy link
Owner

I am not sure why we need this... The current functionality can handle your scenario. Do keep in mind that Backbone Collections are homogeneous bags (even though you may use polymorphism to determine it's type at instantiation or later)

The code snippet below should handle your scenario. Let me know if I am missing something?

var polymorphic = function (relation, attributes) {

    var models = attributes[relation.key];
    // Pick any model to infer type of collection
    var amodel = (models && models.length) ? models[0] : attributes;
    // In case folks pass in Models directly
    if ( amodel instanceof Backbone.Model) return amodel; 
    return amodel ? ( amodel.isBranch ? Tree : Leaf ): Tree;
};

var Tree = Backbone.AssociatedModel.extend({
    relations: [{
        type: Backbone.Many,
        key: 'tree',
        relatedModel:polymorphic
    }]
});

var Leaf = Backbone.AssociatedModel.extend({
});

var t = new Tree({tree:[{isBranch:true},{isBranch:true}]});

equal(t.get('tree').at(0) instanceof Tree, true) //true
equal(t.get('tree').at(1) instanceof Tree, true) //true

var t1 = new Tree({tree:[{isBranch:false},{isBranch:false}]});

equal(t1.get('tree').at(0) instanceof Tree, false) //true
equal(t1.get('tree').at(1) instanceof Tree, false) //true

@dak
Copy link
Contributor Author

dak commented Oct 8, 2013

I think the difference is that Backbone Collections don't have to be homogenous, unless I am misunderstanding you. Below is a snippet from Chrome's Web Inspect of an AssociatedModel (with a 1:M relation for contents) using a polymorphic model on the Collection, per this PR:

contents: Backbone.Collection
    _byId: Object
    _deferEvents: false
    _events: Object
    _pendingEvents: Array[0]
    _proxyCallback: function () {
    _proxyCalls: Object
    length: 7
    model: function (attrs, options) {
    models: Array[7]
        0: Page
        1: Collection
        2: Collection
        3: Page
        4: Page
        5: Page
        6: Page
        length: 7

You can see the collection contains models of type Page and of type Collection.
Backbone documentation for this at: http://backbonejs.org/#Collection-model

@blocka
Copy link

blocka commented Oct 8, 2013

This can be handled with out-of-the-box backbone.

In a recent project I had a collection of Widgets, where each Widget could be a different backbone model.

All I did was something like this:

var Widgets = Backbone.Collection.extend({
    model: function(attributes,options) {
        return new (window[attributes.type+'WidgetModel'])(attributes,options);
    },

@dak
Copy link
Contributor Author

dak commented Oct 8, 2013

@blocka I agree Backbone.Collection supports polymorphic models out of the box. This PR lets 1:M relations take advantage of that functionality.

At the moment, Backbone-associations will execute whatever function you pass to relations and then assign a single model (that must be another AssociatedModel, not a function) to Backbone.Collection's model property.

@blocka
Copy link

blocka commented Oct 8, 2013

I guess if you don't have a strongly-typed collection on hand (like I did), this would be a useful feature.
In fact, my first attempt looked like what you did, and when it failed, I pushed that code back to the collection.

@dhruvaray
Copy link
Owner

@blocka : Thanks!

@dak : I see your point... Thanks for pointing out this out! Would you be so kind to submit a test case (in test/associated-model.js) to the PR before I merge?

dak added a commit to dak/backbone-associations that referenced this pull request Oct 10, 2013
@dak
Copy link
Contributor Author

dak commented Oct 10, 2013

@dhruvaray Added a simple test case. Tried to follow the coding style as closely as possible, but I wrote it pretty late, so apologies if anything is off.

dhruvaray added a commit that referenced this pull request Oct 10, 2013
Allow passing polymorphic models to collections
@dhruvaray dhruvaray merged commit 6572126 into dhruvaray:master Oct 10, 2013
@dhruvaray
Copy link
Owner

Wonderful! Thank you so much! Much appreciated.

@dhruvaray
Copy link
Owner

@dak : Would be nice ( if allowed ) to see your project mention in issue #55!

@dhruvaray
Copy link
Owner

Now available as a recipe on our online documentation : http://dhruvaray.github.io/backbone-associations/recipes.html#tut-pm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants