Linda Keating
Linda Keating

Reputation: 2435

Marionette on adding an item to collection event is firing twice

I'm using Marionette 2.4 and have a layoutView which is listening to an event in the childView. When the event fires I search for an existing model within the collection and if it is not there I create a new model and add it to the collection. If it is found I remove the model from the collection. The problem is that the event seems to be firing twice. The first time it fires, it will create the model, but then as it is firing twice, it then finds the newly created model in the collection and then removes it.

var layout = Marionette.LayoutView.extend({
  childEvents: {
    'channel:selected': 'onChildviewChannelSelected'
  },
  onChildviewChannelSelected: function (childView, args) {
    var linkCollection = this.getRegion('regionWithCollectionView').currentView.collection;
    var modelToUpdate = linkCollection.where({channel: args.currentTarget.value});
    if(modelToUpdate) {
      this.removeModel(linkCollection, modelToUpdate);
    } else {
      this.addModel(linkCollection, args.currentTarget.value);
    }
  },
  removeModel: function (collection, model) {
    collection.remove(model);
  },
  addModel: function (collection, channel) {
    var newEntity = new MyApp.Entities.Link();
    newEntity.set('channel', channel);
    collection.add(newEntity);
  }
});

and here is the child view that fires the 'channel:selected' event....

var childView = Marionette.ItemView.extend({
    events: {
      'change input[type="checkbox"]': 'channelSelected'
    },
    channelSelected: function(args) {
       this.triggerMethod('channel:selected', args);
    }
});

Any idea why the childView fires the 'channel:selected' event twice?

It isn't the view that holds the collection that is being added to, but perhaps there is something that happens when a collection is added to that it will trigger the event again for some reason.

Upvotes: 1

Views: 250

Answers (1)

lucasjackson
lucasjackson

Reputation: 1525

It looks like your function is getting fired twice because of Marionette's "childview* event bubbling". From the documentation:

When a child view within a collection view triggers an event, that event will bubble up through the parent collection view with "childview:" prepended to the event name.

That is, if a child view triggers "do:something", the parent collection view will then trigger "childview:do:something".

This means that "childview:channel:selected" is already being triggered on your layoutview (which means that the onChildviewChannelSelected function is automatically executed on the parent view if it exists http://marionettejs.com/docs/v2.4.7/marionette.functions.html#marionettetriggermethod).

It seems there are a couple potential workarounds. 1 - don't specify a childEvents handler if your handler/function name follows Marionette conventions.

var LayoutView = Marionette.LayoutView.extend({
  template: false,
  el: '.container',
  regions: {
    'regionWithCollectionView': '.collection-view-container'
  },
  onChildviewChannelSelected: function (childView, args) {
    console.log("layoutview::channelSelected - child " + childView.model.get('channel') + " selected");
  }
});

Fiddle showing workaround #1: https://jsfiddle.net/kjftf919/

2 - Rename your LayoutView's childview function handler to something that doesn't conflict with Marionette's automatic event bubbling.

var LayoutView = Marionette.LayoutView.extend({
  template: false,
  el: '.container',
  regions: {
    'regionWithCollectionView': '.collection-view-container'
  },
  childEvents: {
    'channel:selected':'channelSelected'
  },
  channelSelected: function (childView, args) {
    console.log("layoutview::channelSelected - child " + childView.model.get('channel') + " selected");
  }
});

Fiddle showing workaround #2: https://jsfiddle.net/kac0rw6j/

Upvotes: 0

Related Questions