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?
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:
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.
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?
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.