Setting attributes on a collection - backbone js

JavascriptCollectionsbackbone.js

Javascript Problem Overview


Collections in backbone js don't allow you to set attributes, but I often find that there is need to store some meta-information about a collection. Where is the best place to set that information?

Javascript Solutions


Solution 1 - Javascript

Just .extend the collection with a meta data storage function.

var MyCollection = Backbone.Collection.extend({
    initialize: function() {
        ...

        this._meta = {};
    },
    model: ...
    meta: function(prop, value) {
        if (value === undefined) {
            return this._meta[prop]
        } else {
            this._meta[prop] = value;
        }
    },
});

var collection = new MyCollection();
collection.add(someModels);
collection.meta("someProperty", value);

...

var value = collection.meta("someProperty");

There may be better places for storing specific meta data but this depends completely on what the meta data is.

For storing generic meta data extending your collection constructor with a method to do deal with that should work.

Be wary that if this meta data needs to be stored and loaded from the server then you've got a bigger task at hand.

Solution 2 - Javascript

It's probably best to use Collection in exactly the way it was intended: as a bundle of models. (Julien already commented this on the OP, I'd like to give an explanation why I think he is right)

Let's say you are thinking of a Library (collection) of Book (model) as in Backbone's documentation examples. It makes sense that you've got meta information about the library which you want to store, like the address where this book library is located.

The trick is not to think of it as meta information. You've got a library that has a lot of properties, and one of those properties is its collection of books.

var Book = Backbone.Model.extend({ 
    title: "Moby Dick"
});

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

var Library = {
    address: '45th Street',
    collection: Collection
};

In this example I've defined Library as a plain JavaScript object. Obviously you can also make Library be a model, so that it has all the Backbone bells and whistles. My point here is that you need to represent reality in a more realistic way by taking one step back and seeing that extra properties that you want to assign to the Collection are in fact sibling properties of an object one level up: the Library in this case.


Solution 3 - Javascript

I've upgrated Raynos's approach with event triggering, so we can bind to collection's attributes update.

cls.groups = Backbone.Collection.extend({

    // ...

    // Reference to this collection's model.
    model: cls.group,

    initialize: function() {
        this._attributes = {}
    },

    // Extend collection with ability to store attributes and trigger events on attributes changing
    attr: function(prop, value) {
        if (value === undefined) {
            return this._attributes[prop]
        } else {
            this._attributes[prop] = value;
            this.trigger('change:' + prop, value);
        }
    },
    
    // ...

});


cls.group = Backbone.View.extend({

    // ...

    initialize: function() {

        // Catching attribute update
        app.groups.on('change:selected', function(value) {
            // ...
        }, this);
    },

    // ...

    events: {
        'click' : function(e) {
            // Set collection meta attribute on model's view click event
            app.groups.attr('selected', this.model.cid);
        }
    }

    // ...

});

Solution 4 - Javascript

Using the function meta of @Raynos solution with only one parameter do not worked for me. So I've used the following code:

var MyCollection = Backbone.Collection.extend({
    initialize: function() {
        this._meta = {};
    },
    put: function(prop, value) {
        this._meta[prop] = value;
    },
    get: function(prop) {
        return this._meta[prop];
    }
});

var collection = new MyCollection();
collection.put("someProperty", 12);
alert(collection.get("someProperty"));

Hope it'll helps.

Solution 5 - Javascript

I've read the other answers and comments and while I appreciate the notion that wrapping a collection in a model might be the absolute cleanest way to a go, I find it absolute overkill 99.9% of the time. Backbone provides the initialize hook for IMO this exact purpose.

const FooCollection = Backbone.Collection.extend({
    initialize: function(models, attributes) {
        attributes.log && console.log('foo!');  // Or set attributes on 'this', etc
    }
});

// Passing null for first arg, which is optionally an array of models
// to initialize the collection with
const fooCollection = new FooCollection(null, { log: true } );

Been doing this for years and have never encountered any issues/drawbacks.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionidbentleyView Question on Stackoverflow
Solution 1 - JavascriptRaynosView Answer on Stackoverflow
Solution 2 - JavascriptWytzeView Answer on Stackoverflow
Solution 3 - JavascriptAntipinView Answer on Stackoverflow
Solution 4 - Javascriptyves amsellemView Answer on Stackoverflow
Solution 5 - JavascriptMadbreaksView Answer on Stackoverflow