I have to display the same collection of backbone models in 2 different places on the same page (once in the navigation, once in the main area), and I need to keep the models in the collections in sync but allow each collection to be sorted differently. The nav is always alphabetical ascending but the sorting in the main content area is configurable by the user. What's the best way to do this? Should I have 2 different collections and event bindings to try to make sure their models are always identical (using the add/remove/reset events)? Should I have just 1 collection and change it's sort order on the fly as needed?
Asked
Active
Viewed 1,621 times
3
3 Answers
3
I usually consider that the collections should not be altered to represent a transient reordering, so :
- the views listen to the collection for the usual events,
- the views retrieve a custom list of models sorted as needed when they have to render.
For example,
var C = Backbone.Collection.extend({
comparator: function (model) {
return model.get('name');
},
toJSON_sorted: function (reversed) {
var models = this.toJSON();
return (!reversed) ? models : models.reverse();
}
});
var V = Backbone.View.extend({
initialize: function () {
this.collection.on('reset', this.render, this);
this.collection.on('change', this.render, this);
this.collection.on('add', this.render, this);
this.collection.on('delete', this.render, this);
}
});
var NavView = V.extend({
render : function () {
console.log(this.collection.toJSON());
}
});
var MainView = V.extend({
render : function () {
var data = this.collection.toJSON_sorted(this.reversed);
console.log(data);
}
});
Calling these definitions
var c=new C();
var v1 = new NavView({collection:c});
var v2 = new MainView({collection:c});
v2.reversed=true;
c.reset([
{name:'Z'},
{name:'A'},
{name:'E'}
]);
provides the views with the models in the expected order and lets you change the sort direction at will. You could also tailor toJSON_sorted
to handle sorting by other fields.
A Fiddle to play with http://jsfiddle.net/nikoshr/Yu8y8/

nikoshr
- 32,926
- 33
- 91
- 105
-
Oh, interesting technique. I've never used .extend() in that way before... on anything other than Backbone.Model, Backbone.Collection, Backbone.View. If V had a `render()` function, would the extended `render()` in NavView and MainView overwrite the original? I feel like I can use this. – jmk2142 Sep 11 '12 at 17:00
-
1@orangewarp It would indeed mask the original render, but you can always use V.prototype.render.call(this) if you need the parent function – nikoshr Sep 11 '12 at 17:03
-
Thanks. This was a really useful "ah-ha, why didn't I think of that?" Time to reduce some code! :-) – jmk2142 Sep 11 '12 at 17:05
1
If the collections contain exactly the same thing, I think keeping 1 collection and utilizing that for both displays is the most tidy. Just call the sort function before you render whatever visual component you want.

jmk2142
- 8,581
- 3
- 31
- 47
-
I don't think it's that simple. I can call `sort({silent: true})`, but I have to deal with add/remove/reset/sync events. Remove and sync are straightforward, but when a new model is added to the collection, do I change the comparator function on the fly, do a silent `sort` in the event handler for that and then re-render my navigation, then unset the comparator function? Seems pretty brittle. – Peter Lyons Sep 11 '12 at 16:30
-
Maybe I'm just not understanding what you wanted. Since sorting the collection isn't going to automatically sort your views... I was imagining you want something like sortA() then renderA(), then sortB() then renderB(). Actually, maybe the sorting is done inside each render. It made sense to me but if I'm not imagining what you're imagining then moot! ;-) – jmk2142 Sep 11 '12 at 16:42
-
Well, I want the views to stay in sync and bound to the collection, so if a new model shows up or a model is changed, I want current data re-rendered in the views, but just with different sorts. – Peter Lyons Sep 11 '12 at 16:44
-
Gotcha! I'd probably just go with 1 collection then and make the nav and main content two separate views being passed the same collection. In each `render()` I'd resort the collection the way you want it, (or get a sorted ary) then based on that order collection.each( // add view); Each `render()` will completely re-wipe the whole view `this.$el.html()`. Now that there are two views, you can give each their own unique event listeners. `collection.on('add', render)` So when you add or remove, the main view renders with whatever custom sort it's set to and the Nav view renders with the asc sort. – jmk2142 Sep 11 '12 at 16:50
0
All you have to do is change the comparator function. You have have methods on your collection to do this but the basic idea is
var mycollection = new Backbone.Collection();
mycollection.comparator = function(item) { return item.get('blah') }

arhea
- 454
- 2
- 5
-
Yes, I know how to change the sorting of a collection. Did you read the question? Doing this will change the order in both views, which is not what I want. – Peter Lyons Sep 11 '12 at 16:24