0

I am attempting to have a view where after every fetch of 20 different products from my backend (.fetch()), the DOM will display a product ad. The problem I am running into is that every time the collection fires the add event, the CompositeView is re-rendered.

How can I add Children Views to a CompositeView without re-rendering the whole parent?

define(["marionette", "lodash", "text!fonts/products/template.html",
'fonts/products/item-view', 'fonts/products/model', 'eventer'],
function(Marionette, _, templateHTML, ProductItemView, ProductsModel, eventer) {
    'use strict';

    var ProductsView = Marionette.CompositeView.extend({

        template: _.template(templateHTML),

        childView: ProductItemView,

        childViewContainer: '.items',

        productsLimit: 150,

        initialize: function() {
            this.listenTo(eventer, 'sort:products', this.sortCollection, this);
            this.listenTo(this.collection, 'reset sort add', this.render, this);
            this.listenTo(this.collection, 'sync', this.setupSync, this);
        },

        sortCollection: function(field) {
            this.collection.sortByKey(field);
        },

        setupSync: function() {
            this.setupWindowScrollListener();
            this.render();
        },

        productsEnd: function() {
            eventer.trigger('products:end');
        },

        setupWindowScrollListener: function() {
            var $window = $(window),
                $document = $(document),
                that = this,
                collectionSize = that.collection.length;

            if(collectionSize <= that.productsLimit) {
                $window.on('scroll', _.throttle(function() {
                    var scrollTop = $window.scrollTop(),
                        wHeight = $window.height(),
                        dHeight = $document.height(),
                        margin = 200;

                    if(scrollTop + wHeight > dHeight - margin) {
                        eventer.trigger('fetch:more:products');
                        $window.off('scroll');
                    }
                }, 500));
            } else {
                that.productsEnd();
            }
        },
    });

    return ProductsView;
});

Generate Ad Code Here is the code I am using to try to generate ads https://gist.github.com/dennismonsewicz/aecf9bd0befe63e96ee6

dennismonsewicz
  • 25,132
  • 33
  • 116
  • 189
  • Do you have a CompositeView filter in the view? – seebiscuit Jul 09 '15 at 10:37
  • Are you invoking sort after you add a child? – seebiscuit Jul 09 '15 at 12:08
  • @seebiscuit - I have updated my question with my CompositeView – dennismonsewicz Jul 09 '15 at 14:26
  • @seebiscuit - Was wrangling with it this morning. I removed my call to re-render the view after a `sync` or `add` or `sort`. Just not sure how to insert an ad after every 20 items are fetched (the API is set to pull back 20 at a time) – dennismonsewicz Jul 10 '15 at 14:17
  • You don't have to do anything. Your CompositeView is listening for `add` events on your `collection`. When a fetch brings in new models, the CompositeView will render these new children and insert them in their sort order (defined by the `collection`'s `comparator`). Try it. – seebiscuit Jul 10 '15 at 14:38
  • @seebiscuit - Yes, that works. I'm struggling with the Ad portion of this app. Not sure how to insert one into the DOM after every 20 products are fetched (and keep the ad there during sorting). – dennismonsewicz Jul 10 '15 at 15:56
  • Look at the last bullet on my answer: pass `{ reorderOnSort: true }` to your `CompositeView` on instantiation. – seebiscuit Jul 10 '15 at 16:06
  • @seebiscuit - I added a gist to my question with the AdGeneration portion (the reorderOnSort option is working by the way). I made a comment or two on some issues I'm now getting – dennismonsewicz Jul 10 '15 at 18:02
  • Looks good. Why don't you accept my answer and put this up on a second question. Let me know and I'll take a closer look. – seebiscuit Jul 10 '15 at 18:08
  • @seebiscuit - new question http://stackoverflow.com/questions/31347648/marionette-compositeview-children-findbyindex-not-working-as-expected-after-coll – dennismonsewicz Jul 10 '15 at 18:18

1 Answers1

2

What's causing your parent view to re-render, is that you're calling a render on a collection add event :)

this.listenTo(this.collection, 'reset sort add', this.render, this);

Unless you really want to re-render that parent CompositeView you don't need this listener at all because Marionette handles:

  • collection#add: _onCollectionAdd will render and place new childviews in the default (based on a comparator, if none then id) sort order
  • collection#reset: Marionette already has a listener on your CompositeView that will call this.render for you if your collection is reset
  • collection#sort: If you provide your view { sort: true } as an option to your CompositeView, it will handle 'sort' events on your collection. The default is to sort the children views and then call render on the CompositeView. If you don't want your CompositeView to be re-rendered, pass { reorderOnSort: true } and the children view will be efficiently reordered without re-rendering anything (parent nor children), simply moving the children DOM elements in the sort order dictated by the collection.comparator.
seebiscuit
  • 4,905
  • 5
  • 31
  • 47