0

I am working with an app that after every collection.fetch, I need to drop in a random ad into the DOM. But, every time the collection fetches, and the ad is dropped in, it appears that the DOM is resetting itself instead of just appending new items to the overall collection container.

Here is the ItemView for the ad:

define(["marionette", "lodash", "text!ads/template.html", "eventer"],
function(Marionette, _, templateHTML, eventer) {
    'use strict';

    var AdsView = Marionette.ItemView.extend({
        template: _.template(templateHTML),

        ui: {
            ad: '.ad'
        },

        initialize: function() {
            this.listenTo(eventer, 'generate:new:ad', this.generateNewAd, this);
        },

        onShow: function() {
            // Set add image onShow
            this.ui.ad.prop('src', '/ad/' + this.randomNumber());
        },

        generateNewAd: function(childView) {
            var newAd = this.ui.ad.clone(),
                element = childView.$el,
                elementId = childView.model.get("id");

            newAd.prop('src', '/ad/' + this.randomNumber());

            $("#" + elementId).after(newAd);
        },

        randomNumber: function() {
            return Math.floor(Math.random()*1000);
        },

        setUpAd: function() {
            this.ui.ad.prop('src', '/ad/' + this.randomNumber());
        }
    });

    return AdsView;
});

CompositeView that holds the Products (I'm calling for a new ad after the collection is done syncing):

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, 'sync', this.setupSync, this);
        },

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

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

        adGeneration: function() {
            var child = this.children.last();
            eventer.trigger('generate:new:ad', child);
        },

        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;
});
dennismonsewicz
  • 25,132
  • 33
  • 116
  • 189

1 Answers1

0

From your previous question I noticed that you're passing { reorderOnSort: false } to your ProductsView. This will cause your CompositeView to re-render on a sort event. Since a 'sort' event is triggered by Collection.set(), you'll have to pass { reorderOnSort: true } to ensure that your CompositeView is not re-rendered after a fetchsetsort.

Note: If your CompositeView defines a filter method and the children are filter'ed when new models are fetched, the CompositeView will re-render.

Community
  • 1
  • 1
seebiscuit
  • 4,905
  • 5
  • 31
  • 47
  • Hmm.. I wonder if the better approach is to grab an extremely large number of products at once and then show them as the user scrolls. Just not sure how to do that – dennismonsewicz Jul 13 '15 at 14:56
  • Maybe, but keep in mind that large downloads may quickly degrade the user experience, and while handling the rendering isn't terribly difficult, (your `setupWindowScrollListener` method has a lot of the low-end machinery necessary) it's only complicate the code. I think what you're doing now is perfectly acceptable. – seebiscuit Jul 13 '15 at 15:31
  • Awesome, thanks! What's happening right now, is that, when the fetch happens, the DOM re-renders and an ad is generated and placed after the last child. Sorry if you've tried to explain how to fix this multiple times – dennismonsewicz Jul 13 '15 at 16:01
  • And this is after your instantiated `ProductsView` with the option `{ reorderOnSort: true }`? – seebiscuit Jul 13 '15 at 16:06
  • Yep... `this._productsView = new ProductsView({ reorderOnSort: true, collection: new ProductsCollection() });` – dennismonsewicz Jul 13 '15 at 16:52
  • Can you show me the code for `collection.sortByKey()`? – seebiscuit Jul 13 '15 at 16:56
  • `sortByKey: function(field) { this.sort_key = field; this.sort(); },` – dennismonsewicz Jul 13 '15 at 17:04
  • How does setting `this.sort_key` help you sort the children? Did you override `Collection.prototype.sort`? – seebiscuit Jul 13 '15 at 20:29
  • Well, the user can select a sort option from a drop down and when they select an option, an event is emitted to resort the collection – dennismonsewicz Jul 13 '15 at 21:24
  • I'm not sure `Collection.sort` will use `Collection.sort_key` to sort its models. You should look at [`CollectionView.viewComparator`](http://marionettejs.com/docs/v2.4.2/marionette.collectionview.html#collectionviews-viewcomparator) – seebiscuit Jul 13 '15 at 21:31