I\'m using a Signalr hub to subscribe to events on the server. What an event is dispatched to a hub, its successfully adding the item to a Marionette CollectionView. This, in
Usually you'll have the rendering take place in your Backbone.View
'subclass'. So you have something like:
render: function() {
this.collection.each( function(model) {
// some rendering of each element
}, this );
}
this.collection
is presumably a Backbone.Collection
subclass, and so you can just use underscore.js methods on it to get it in whatever order you like:
this.collection.reverse().each( ... )
this.collection.sort( function(m) { ... } ).each( ... )
Etc.
Of course, you are getting a single element from your backend, and you want to insert it in the right place without re-rendering the whole thing! So in that case just go old school and insert your sort key as a rel
attribute or data
attribute on the elements, and use that to insertAfter
or similar with jQuery in your renderNewItem
(or similar) method.
Backbone automatically keeps Collections in sorted order. If you want to use a non-default sort, define a comparator()
function on your Collection and it will use that instead. The comparator can take either one or two arguments, see the Backbone documentation for details.
You can then render your collection in an .each() loop, and it will come out in the correct order. Adding new items to the view in sorted order is up to you, though.
To go through collection in the reverse order I usually use a construction like this:
_.each(collection.last(collection.length).reverse(), function(model){ });
There is a thread on this topic at https://github.com/marionettejs/backbone.marionette/issues/78
Although Backbone keeps the collection sorted once you define a comparator, as @breischl pointed out, Marionette does not automatically re-render the CollectionView when order changes. In fact, Marionette listens to the add
event on the collection and appends a new ItemView.
If you want your CollectionView
to always display items in reverse chronological order, and you want new items added to be prepended instead of appended, then override the appendHtml
method in your CollectionView
as follows:
var MyCollectionView = Backbone.Marionette.CollectionView.extend({
appendHtml: function(collectionView, itemView){
collectionView.$el.prepend(itemView.el);
}
});
If you want to be able to insert at a particular location as @dira mentioned in the comment, there is a solution posted at the link above on github by sberryman that I reproduce here for convenience (disclaimer: I haven't tested the code below personally):
Change appendHtml to:
appendHtml: function(collectionView, itemView) {
var itemIndex;
itemIndex = collectionView.collection.indexOf(itemView.model);
return collectionView.$el.insertAt(itemIndex, itemView.$el);
}
And add the following to extend jQuery
to provide insertAt
function:
(function($) {
return jQuery.fn.insertAt = function(index, element) {
var lastIndex;
if (index <= 0) return this.prepend(element);
lastIndex = this.children().size();
if (index >= lastIndex) return this.append(element);
return $(this.children()[index - 1]).after(element);
};
})(jQuery);