I have created very quick example of alert triggering twice on backbone View.
http://jsfiddle.net/feronovak/8svqX/
This is does nothing special, it really is for me to understand how to call separate Views correctly, so they dont trigger previous click method. Right now I generate two Views, second View overwrites form in #boxSearch. When I click button Search it also generates alert doSearch SearchLocation
. I was expecting to see only alert doSearch SearchLatitude
. What I have done wrong?
var Geo = {
Views: {},
Templates: {}
};
Geo.Templates.SearchLocation = _.template( $("#tpl-search-location").html());
Geo.Templates.SearchLatitude = _.template( $("#tpl-search-latitude").html());
Geo.Views.SearchLocation = Backbone.View.extend({
initialize: function() {
this.render();
},
el: $("#boxSearch"),
template: Geo.Templates.SearchLocation,
render: function()
{
$(this.el).html(this.template);
},
events: {
"click input[type=button]": "doSearch"
},
doSearch: function(e) {
e.preventDefault();
alert('doSearch SearchLocation');
}
});
Geo.Views.SearchLatitude = Backbone.View.extend({
initialize: function() {
this.render();
},
el: $("#boxSearch"),
template: Geo.Templates.SearchLatitude,
render: function()
{
$(this.el).html(this.template);
},
events: {
"click input[type=button]": "doSearch"
},
doSearch: function(e) {
e.preventDefault();
alert('doSearch SearchLatitude');
}
});
$(document).ready(function(e) {
var searchLocation = new Geo.Views.SearchLocation();
var searchLatitude = new Geo.Views.SearchLatitude();
});
This is the "ghost view" problem often seen with Backbone apps:
As @machineghost points out, there's nothing in your code to unbind the first view, so both views are attached to #boxSearch
, and both views will respond to the click
event. There are several ways around this:
You could call e.stopPropagation()
(docs) in the event handlers (I think this is what you're trying to do with e.preventDefault()
). This will prevent the event from bubbling up once it's triggered, so the last defined view would catch it. This works, but note that you still have the two views hanging around, and the outdated ghost view might have other side-effects.
You could have each view render its own DOM element, with a class .boxSearch
, and then call view.remove()
on the one you no longer want. This is probably the cleanest option, but it means creating most of your DOM on the fly, which is a bit more expensive and hard to manage than coding it in HTML.
You can give each view a cleanup method, e.g. .clear()
or .exit()
, which could call view.undelegateEvents()
and view.stopListening()
. Then make sure you call this method when you are done with a view.
If you're careful about making sure it gets called and making sure it clears everything that needs to be cleared, then it works well.
When you add another view to an element, it doesn't automatically remove the old one; if you want to do that you have to explicitly call yourFirstView.remove()
. However, doing that will remove the element as well; if you just want to unbind the events without removing the element you can use yourFirstView.undelegateEvents()
instead.
It's worth mentioning that this is a deliberate behavior of Backbone: oftentimes it's helpful to be able to have 2+ views on a single el
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With