Search code examples
backbone.jsmarionettebackbone-views

Marionette CompositeView Sort Rendering


I have a Marionette (2.4.1) CompositeView and when I do a sort it re-renders the entire view rather than the childView. The header icons revert back. I could fix them on render but is there a way that I can just render the childView?

diaryEntries = Backbone.Marionette.CompositeView.extend({
  template  : diaryEntries,
  className: 'diary-entries',
  collection: new Diary(),
  childViewContainer: 'tbody',
  reorderOnSort: true,

  events: {
    'click th[data-sort]': 'sort',
    'click .pagination a': 'paginate'
  },

  initialize: function() {
    this.itemsPerPage = 5;
    this.currentPage = 1;
    this.pages;
  },

  ...

  sort: function(e) {
    var $th, dir, sort, sorted;
    e.preventDefault();
    $th = $(e.currentTarget);
    sort = $th.data('sort');

    if (sort === this.collection.sortField) {
      this.collection.sortDirection *= -1;
    } else {
      this.collection.sortDirection = 1;
    }

    this.collection.sortField = sort;

    $('span.glyphicon').removeClass('active-sort');
    $th.siblings('th').find('span.glyphicon').removeClass('glyphicon-chevron-down glyphicon-chevron-up').addClass('glyphicon-sort');

    if (this.collection.sortDirection === 1) {
      $th.find('span.glyphicon').removeClass('glyphicon-chevron-down glyphicon-sort').addClass('glyphicon-chevron-up active-sort');
    } else {
      $th.find('span.glyphicon').removeClass('glyphicon-chevron-up glyphicon-sort').addClass('glyphicon-chevron-down active-sort');
    }

    this.collection.sort();
  },
...

});

Solution

  • Well, looks like Marionette was concerned about the same thing you are. I couldn't find this in the docs, but it's pretty plain in the source. If you pass this option:

    reorderOnSort: true
    

    into your Collection/Composite view, on a 'sort' event the Collection/View will not re render, just its children.

    See this line in the Marionette source: https://github.com/marionettejs/backbone.marionette/blob/v2.4.1/src/collection-view.js#L166

    UPDATE If you're filtering your children views, running sort on your collection will invoke render on the Collection/CompositeView. The logic is that if you're paginating your children results, then you must sort the original, unfiltered, collection to properly display paginated results.

    Nonetheless, I don't see anything intrinsically wrong with paginating a filtered set.

    Fortunately, its easy to override the sort method to render whether your results are filtered or not. On you Collection/CompositeView include this method:

    reorder: function() {
       var children = this.children;
       var models = this._filteredSortedModels();
    
       // get the DOM nodes in the same order as the models
       var els = _.map(models, function(model) {
         return children.findByModel(model).el;
       });
    
       this.triggerMethod('before:reorder');
       this._appendReorderedChildren(els);
       this.triggerMethod('reorder');
       }
    },