idbentley
idbentley

Reputation: 4218

Setting attributes on a collection - backbone js

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?

Upvotes: 56

Views: 29128

Answers (5)

Madbreaks
Madbreaks

Reputation: 19539

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.

Upvotes: 0

Wytze
Wytze

Reputation: 7904

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.


Upvotes: 17

Antipin
Antipin

Reputation: 71

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);
        }
    }

    // ...

});

Upvotes: 7

Raynos
Raynos

Reputation: 169383

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.

Upvotes: 44

yves amsellem
yves amsellem

Reputation: 7234

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.

Upvotes: 4

Related Questions