jason jason - 6 months ago 16
Javascript Question

Backbone.js ListenToOnce gets called twice

I have a view (I'll call parent) that has another view inside of it ( a child view). The parent view has elements that use a "context menu" event (when right clicked, a set of menu options show up). When a user selects an item from the menu, it fires a callback. From there, I trigger an event in the child view. Here is my trigger:

that.trigger("editFileFolder", {fileType: fileType});


In my child view's initialize function, I have this:

this.listenToOnce(this.options.parent,'editFileFolder', this.editFileObjectEvent);


The editFileObjectEvent function calls another view to be created (a dialog with some fields, a cancel button and a save button). If the user clicks cancel, everything works fine. If the use clicks the save button, the view does an ajax call, saves to the server, and closes the dialog. But, the next time the user right clicks and selects the same menu item, the editFileObjectEvent gets called twice (resulting in the dialog being added twice to the parent view).

Can anyone explain why it's getting called twice and how to resolve this? If you wish to see specific code, let me know and I can add it. There's a lot, so I don't want to overwhelm the question.

thanks

Answer

I think the function that calls this.listenToOnce is called twice. You should try to avoid this. However if this is not possible you can make sure the listeners is only bound once by unbinding it before binding:

this.stopListening(this.options.parent, 'editFileFolder', this.editFileObjectEvent);
this.listenToOnce(this.options.parent, 'editFileFolder', this.editFileObjectEvent);

Or you could have a property on your instance to prevent from binding twice:

if (!this.__boundListener) {
  this.listenToOnce(this.options.parent, 'editFileFolder', this.editFileObjectEvent);
  this._boundListener = true;
}

All listeners are registered in this._listeningTo and you might be able to search though that to check if the event is already bound.

You can also refactor your code:

MyModel.extend({
  initialize: function() {
    this.bindListenToOnce = _.once(this.bindListenToOnce);
  },
  abc: function() {
    // do stuff
    this.bindListenToOnce();
    // do stuff
  },
  bindListenToOnce: function() {
    this.listenToOnce(this.options.parent, 'editFileFolder', this.editFileObjectEvent);
  }
});
Comments