Ok, so I have a navigation and the concept of a dashboard. In my router when I click on a navigation link, I'm calling on my dashboard view to set the active tab.
For example:
<a href="#" class="dashabord_tab" id="widgets">Widgets</a>
<a href="#" class="dashabord_tab" id="Foobars">Foobars</a>
<div id="nav"></div>
<div id="current_tab"></div>
DashboardView = Backbone.View.extend({
template:JST.dashboard_container,
render: function(){
$(this.el).html(this.template());
},
activateWidgets: function(){
if(!(this.widgets_view)){
this.widgets_view = new WidgetsView();
this.widgets_view.render();
}
$(this.el).find("#current_tab").html(this.widgets_view.el);
},
activateFoobars: function(){
if(!(this.foobars_view)){
this.foobars_view = new FooBarsView();
this.foobars_view.render();
}
$(this.el).find("#current_tab").html(this.foobars_view.el);
}
});
So the first time I switch to either widgets_tab or foobar_tab the tab loads as expected. However if I switch to a tab, switch out of it, then switch back to it, all the events in my view are no longer bound. Clicking, hoevering, none of the views inside foobars_view for example are clickable, hoverable, etc.
In the past, I have been making a wrapper for each one of the child views like so:
activateFoobars: function(){
$('.tab_wrapper').hide();
if($(this.el).find("#foobars_wrapper").length < 1){
$(this.el).append("<div class='tab_wrapper' id='foobars_wrapper'></div>");
this.foobars_view = new FooBarsView();
$(this.el).find("#foobars_wrapper").html(this.foobars_view.render().el);
}
$('#foobars_wrapper').show();
}
I'd rather not do it the way I just showed above, and I really like the idea of just putting the view's el in an active_tab div that always stays open... If I can't do it the way I'd like to, is there an alternative to the way I just showed above? Thanks!
When you use .html(dom_object)
:
$(this.el).find("#current_tab").html(this.widgets_view.el);
You're actually doing this:
$(this.el).find('#current_tab').empty().append(this.widgets_view.el);
and empty
:
removes other constructs such as data and event handlers from the child elements before removing the elements themselves [To avoid memory leaks]
So once you've added, say, widgets_view
to #current_tab
and then .html(this.foobars_view.el)
, the events on widgets_view.el
are gone. Your .html
call works fine the first time because there's nothing in #current_tab
so there aren't any events to unbind.
You can use your "reuse the views" approach by adding simple delegateEvents
calls into the mix to rebind the events:
activateWidgets: function(){
if(!(this.widgets_view)){
this.widgets_view = new WidgetsView();
this.widgets_view.render();
}
$(this.el).find("#current_tab").html(this.widgets_view.el);
this.widgets_view.delegateEvents(); // <--------------- you need this
}
While I'm here, you can use this.$('#current_tab')
instead of $(this.el).find('#current_tab')
and this.$el
instead of $(this.el)
.
Demo: http://jsfiddle.net/ambiguous/75KuW/1/
Note that switching tabs in the above preserves the content of the <input type="text">
elements. If you don't need that sort of behavior then you're probably better off removing and instantiating views as needed instead of holding all the views and swapping them in and out.
Also, if your WidgetsView
(and friends) have other Backbone views inside them, you'll have to call delegateEvents
on the contained views all the way down. In this case, some sort of bind_events
method on the views would make sense:
this.widgets_view.rebind_events(); // delegateEvents() all the way down this view subtree
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