56

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?

4
  • I haven't used Backbone, so a comment rather than an answer: It looks like Backbone collections are JavaScript objects (hard to imagine what else they would be), which means that unless they're sealed (a new ECMAScript5 thing), you can just add properties to them if you like in the normal way: instance.prop = value;. Naturally you have to be careful not to use names that conflict with anything that already exists on the object... Commented May 8, 2011 at 22:10
  • The answer is completely dependant on what kind of meta information it is. Depending on what your storing it might fit on the collection or on the view. Commented May 8, 2011 at 22:33
  • @T.J. Crowder - I could do that, but it has a lot of potential problems. Having a dedicated Object just for attributes means that you can be much safer, I think. Commented May 8, 2011 at 23:17
  • I would create an object that would have the collection as a property. Commented Sep 7, 2012 at 14:36

5 Answers 5

44

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.

Sign up to request clarification or add additional context in comments.

10 Comments

Thanks for that - helps a lot. Your last point highlights something I've been wanting to do as well... I'll ask another question regarding that though, as your answer here is exactly what I've been wanting.
@idbentley if you highlight exactly what your meta data that needs to be persistantly stored is I can point you in the right direction.
I asked a new question here with details: stackoverflow.com/questions/5931898/…
It turns out that this isn't really the best way to do this. Instead, wrap the collection in a model. In fact, if I had read the documentation carefully at the time I would have known this as they give an example of it here: documentcloud.github.com/backbone/#FAQ-nested . The meta information lives in the wrapping model. This should help.
The meta function is not contributing much. You could have used the _meta directly (view._meta.a = 'b' and var c = view._meta.a). Other than that, @idbentley's comment is a better solution.
|
17

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.


4 Comments

Thanks for the better answer. I posted a seperate question which made this obvious to me. stackoverflow.com/questions/5931898/… I encourage anyone to read the conversation in that question; I found it very clarifying.
@Wytze : Is it correct if I declare Library as another backbone model?
The OP's question seems important for things like API keys, user ids, etc. that are necessary to construct the url used by fetch().
In the above example, let's say I have a Book view that should also show the address this book is currently located. How would you do that? Something like this.model.collection.owner.address ? I don't like it much.
7

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

    // ...

});

1 Comment

Have a look at this question for an alternative. Essentially, wrapping the collection in a model allows a lot more flexibility without hacking a lot of cruft into the collection. stackoverflow.com/questions/5931898/…
4

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.

1 Comment

The reason why it didnt work was because there was a bug. !== instead of ===
0

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.

1 Comment

This works well Madbreaks, no doubt. The reason that I avoided this solution was because I wanted to be able to use fetch with no modifications. Check out this question, which explains what I'm saying in more detail: stackoverflow.com/questions/5931898/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.