Better solution for nested Backbone.js collections

Simen Brekken picture Simen Brekken · May 2, 2012 · Viewed 8.2k times · Source

Many of my Backbone models often deal with nested models and collections, so far I'm using a combination of defaults, parse and toJSON manually to achieve nesting:

ACME.Supplier = Backbone.Model.extend({
    defaults: function() {
        return {
            contacts: new ACME.Contacts(),
            tags: new ACME.Tags(),
            attachments: new ACME.Attachments()
        };
    },

    parse: function(res) {
        if (res.contacts) res.contacts = new ACME.Contacts(res.contacts);
        if (res.tags) res.tags = new ACME.Tags(res.tags);
        if (res.attachments) res.attachments = new ACME.Attachments(res.attachments);

        return res;
    }
});

ACME.Tag = Backbone.Model.extend({
    toJSON: function() {
        return _.pick(this.attributes, 'id', 'name', 'type');
    }
});

I've looked at a few plugins out there that basically does the same as above but with a lot less control and more boilerplate, so I'm wondering if anyone has a more elegant solution to this common Backbone.js problem.


Edit: I ended up with the following approach:

ACME.Supplier = Backbone.Model.extend({
    initialize: function(options) {
        this.tags = new ACME.Tags(options.tags);
    },

    parse: function(res) {
        res.tags && this.tags.reset(res.tags);

        return res;
    }
});

ACME.Tag = Backbone.Model.extend({
    toJSON: function() {
        return _.pick(this.attributes, 'id', 'name', 'type');
    }
});

It is worth noting that later I discovered that you'll need to pass nested model/collection data from the constructor into the constructor of the nested model via the options object.

Answer

fguillen picture fguillen · May 2, 2012

I don't see any problem with your approach.

IMHO the Model.parse() method if for this: to be overwritten in case special parse behavior needs.

The only think I'd change would be things like this:

if (res.tags) res.tags = new ACME.Tags(res.tags);

For this:

if (res.tags) this.tags.reset(res.tags);

Due you already have an instance of ACME.Tags collection I'd reuse it.

Also I don't really like the defaults implementation, I'm used to do this initializations in the Model.initialize() but I think is a matter of taste.