I am trying to understand backbone and am currently struggling with zombie views. I have read many stack overflow posts on the matter but I still cannot figure it out.
For the sake of simplicity, I set up two views (without data) that I need to switch. What I did so far was:
- creating an object
//define application object var app = { vent: {}, templates: {}, views: {}, routers: {}, }; //instantiate event aggregator and attach it to app app.vent = _.extend({}, Backbone.Events);
defining two very simple templates (stored into app.templates): the first one has some dummy text and a button (with and id of 'test-begin'), the second one just dummy text
defining two views
app.views.instructions = Backbone.View.extend({ //load underscore template template: _.template(app.templates.instructions), //automatically called upon instantiation initialize: function(options) { //bind relevant fucntions to the view _.bindAll(this, 'render', 'testBegin', 'stillAlive', 'beforeClose'); //listen to app.vent event this.listenTo(app.vent, 'still:alive', this.stillAlive); }, //bind events to DOM elements events: { 'click #test-begin' : 'testBegin', }, //render view render: function() { this.$el.html(this.template()); return this; }, //begin test testBegin: function() { Backbone.history.navigate('begin', {trigger: true}); }, //still alive stillAlive: function() { console.log('I am still alive'); }, //before closing beforeClose: function() { //stop listening to app.vent this.stopListening(app.vent); }, }); //test view app.views.test = Backbone.View.extend({ //load underscore template template: _.template(app.templates.test), //automatically called upon instantiation initialize: function(options) { //trigger still:alive and see if removed view responds to it app.vent.trigger('still:alive'); //bind relevant fucntions to the view _.bindAll(this, 'render'); }, //render view render: function() { this.$el.html(this.template()); return this; }, });
- defining a router
//base router app.routers.baseRouter = Backbone.Router.extend({ //routes routes: { '': "instructions", 'begin': "beginTest" }, //functions (belong to object controller) instructions: function() {baseController.instructions()}, beginTest : function() {baseController.beginTest()}, }); //baseRouter controller var baseController = { instructions: function() { mainApp.viewsManager.rederView(new app.views.instructions()); }, beginTest: function(options) { mainApp.viewsManager.rederView(new app.views.test()); }, };
- defining mainApp (with a view-switcher)
//define mainApplication object mainApp = {}; //manages views switching mainApp.viewsManager = { //rootEl rootEl: '#test-container', //close current view and show next one rederView : function(view, rootEl) { //if DOM el isn't passed, set it to the default RootEl rootEl = rootEl || this.rootEl; //close current view if (this.currentView) this.currentView.close(); //store reference to next view this.currentView = view; //render next view $(rootEl).html(this.currentView.render().el); }, }; //render first view of app mainApp.viewsManager.rederView(new app.views.instructions()); //initiate router and attach it to app mainApp.baseRouter = new app.routers.baseRouter(); //start Backbone history Backbone.history.start({silent: true });
- adding a close function to view via Backbone prototype
//add function to Backbone view prototype (available in all views) Backbone.View.prototype.close = function () { //call view beforeClose function if it is defined in the view if (this.beforeClose) this.beforeClose(); //this.el is removed from the DOM & DOM element's events are cleaned up this.remove(); //unbind any model and collection events that the view is bound to this.stopListening(); //check whether view has subviews if (this.hasOwnProperty('_subViews')) { //loop thorugh current view's subviews _(this._subViews).each(function(child){ //invoke subview's close method child.close(); }); } };
So, in order to check for zombie views, the second view triggers and event (still:alive) that the first view listen to and respond to it via a message sent to the console.log (although it really shouldn't). The first view does listen to such a message (in the console log I read 'I am still alive) even when it has been replaced by the second view.
Can you help me? thank you very.