user1775725
user1775725

Reputation: 81

BackboneJS collection add event fired on multiple collections

I am running into an issue where I'm adding to a collection, CollectionA, where CollectionA and CollectionB are listening for an add event. However, an add event is triggered for both collections when adding to CollectionA. CollectionA and CollectionB are basic collections that extend Backbone.Collection and only setting a url.

View Definitions

ModelViewA = Backbone.View.extend({
    ...
    render : function() {
        var self       = this;
        var attributes = this.model.toJSON();
        this.$el.html(this.template(attributes));

        var collectionB     = new CollectionB();
        var collectionViewB = new CollectionViewB();
        collectionB.fetch({
            self.$el.append(collectionViewB.render().el);
        });

        return this;
    },
    ...
});

CollectionViewA = Backbone.View.extend({
    initialize : function() {
       this.collection.on('add', this.renderOne, this);
    },
    ...
    render : function() {
       this.collection.forEach(this.renderOne, this);
       return this;
    },

    renderOne : function(model) {
        console.log('Added to Collection A');

        var view = new ViewA({ model : model });
        this.$el.append(view.render().el);
        return this;
    }
});

CollectionViewB = Backbone.View.extend({
    initialize : function() {
       this.collection.on('add', this.renderOne, this);
    },
    ...
    render : function() {
       this.collection.forEach(this.renderOne, this);
       return this;
    },

    renderOne : function(model) {
        console.log('Added to Collection B');

        var view = new ViewB({ model : model });
        this.$el.append(view.render().el);
        return this;
    }
});

Here's how I'm starting off the cascade of these events...

var collectionA = new CollectionA();
var modelViewA = new ModelViewA({
    model : collectionA.at('some id');
});

$('body').append(modelViewA.render().el);
$('body').empty();

var collectionViewA = new CollectionViewA({ collection: collectionA });
$('body').append(collectionViewA.render().el);
collectionViewA.add([{...}]);

This code will output the initial view correctly with the modelView. Then it will properly render the collectionViewA, however when I trigger the add event it will try to add to CollectionA and CollectionB so the console will show the following for each time the add event is called

Added to Collection B
Added to Collection A

It will also attempt to render Collection B, but throws an error because the template is not given a model.

Let me know if this is way too confusing and I can try to flush out this description a bit more.

THANKS!

Upvotes: 4

Views: 704

Answers (1)

JayC
JayC

Reputation: 7141

Why, in your render method of ModelViewA, do you create a CollectionB instance that fetches?

    var collectionB     = new CollectionB();
    var collectionViewB = new CollectionViewB();
    collectionB.fetch({
        self.$el.append(collectionViewB.render().el);
    });

That doesn't even parse. Are you sure you didn't accidentally edit out the anonymous success function? First let me suppose that's not quite what your code shows. That is, is it supposed to be something like:

    collectionB.fetch({
        success: function(){
           self.$el.append(collectionViewB.render().el);
        }
    });

?

Let's go with that.

So when the ModelViewA is rendered, obviously collectionB is going to fetch something and then collectionViewB is going to render because that's what you told it to do. Now would cause you to render ModelViewAs?

CollectionViewA = Backbone.View.extend({
    initialize : function() {
       this.collection.on('add', this.renderOne, this);
    }, ...

renderOne : function(model) {
    console.log('Added to Collection A');

    var view = new ViewA({ model : model });
    this.$el.append(view.render().el);
    return this;
}

what's this ViewA? could that also refer to ModelViewA?

If so, we might guess as to what is happening:

Your CollectionViewA instance renders a ModelViewA instance on every addition. Every rendering of a a ModelViewA instance creates a CollectionB instance (collectionB) and a CollectionViewB instance (collectionViewB), and the collectionB fetch has a success method which renders collectionViewB. If collectionB has any models, for every model, it successfully logs to the console "Added to Collection B" before attempting to render ViewB instances, which, if I understand you correctly, fail (which might be why you only have one "Added to Collection B" message.

Now as to why the console logs are in reverse order... I'm still trying to figure that one out. Maybe I missed something.

In any case... This situation is not ideal. I'm not sure what I could say to improve your code, though. Calling fetch from render seems to me like you're specifying to much to do in your render code. I'm not even sure you knew that fetch was there to begin with.

Upvotes: 1

Related Questions