Backbone events are firing multiple times after re

2019-04-10 23:41发布

We have a single Backbone view comprised of a sidebar and several sub-views. For simplicity, we've decided to have the sidebar and sub-views governed by a single render function. However, the click .edit event seems to be firing multiple times after clicking on one of the sidebar items. For example, if I start out on "general" and click .edit, then hello fires once. If I then click .profile on the sidebar and click .edit again, hello fires twice. Any ideas?

View

events: {
  "click .general": "general",
  "click .profile": "profile",
  "click .edit": "hello",
},

general: function() {
  app.router.navigate("/account/general", {trigger: true});
},

profile: function() {
  app.router.navigate("/account/profile", {trigger: true});
},

render: function(section) {
  $(this.el).html(getHTML("#account-template", {}));
  this.$("#sidebar").html(getHTML("#account-sidebar-template", {}));
  this.$("#sidebar div").removeClass("active");
  switch (this.options.section) {
    case "profile":
      this.$("#sidebar .profile").addClass("active");
      this.$("#content").html(getHTML("#account-profile-template"));
      break;
    default:
      this.$("#sidebar .general").addClass("active");
      this.$("#content").html(getHTML("#account-general-template"));
  }
},

hello: function() {
  console.log("Hello world.");
},

Router

account: function(section) {
  if (section) {
    var section = section.toLowerCase();
  }
  app.view = new AccountView({model: app.user, section: section});
},

Solution

My solution was to change the router to this:

account: function(section) {
  if (section) {
    var section = section.toLowerCase();
  }
  if (app.view) {
    app.view.undelegateEvents();
  }
  app.view = new AccountView({model: app.user, section: section});
},

This works for now, but will this create a memory leak?

3条回答
你好瞎i
2楼-- · 2019-04-10 23:59

I had exactly the same problem when I first started using backbone. Like Peter says, the problem is that you have more than one instance of the View being created and listening for the event. To solve this, I created this solution in my last backbone project:

/* Router view functions */
showContact:function () {
    require([
        'views/contact'
    ], $.proxy(function (ContactView) {
        this.setCurrentView(ContactView).render();
    }, this));
},
showBlog:function () {
    require([
        'views/blog'
    ], $.proxy(function (BlogView) {
        this.setCurrentView(BlogView).render();
    }, this));
},


/* Utility functions */
setCurrentView:function (view) {
    if (view != this._currentView) {
        if (this._currentView != null && this._currentView.remove != null) {
            this._currentView.remove();
        }
        this._currentView = new view();
    }
    return this._currentView;
}

As you can see, it's always removing the last view and creating a new one, which then renders. I also add a require statement in the router because I don't want to have to load all views in the router until they are actually needed. Good luck.

查看更多
小情绪 Triste *
3楼-- · 2019-04-11 00:20

Sounds like you are attaching multiple view instances to the same DOM element and they are all responding to the events. Are you making a new view each time you navigate without removing the previous view?

查看更多
神经病院院长
4楼-- · 2019-04-11 00:20

I have a dynamic view, that renders different templates inside the same element (about 12), based on router params. Now, the container in which the view renders, is defined inside the view.render() like so "el: '#some-container'". Naturally, i have to remove the view if it exists, before creating a new or the same one, to prevent zombies and s#!t. Just as a reminder, calling view.remove(), actually removes '#some-container' from the DOM, meaning the view has no place to render in, except for the first time. Now, there are dozens of methods to prevent this from happening. Just thought i should share in case anyone needs to save a few hours of research.

查看更多
登录 后发表回答