Back to the Backbone Zombie Views

I am trying to understand the spine, and currently I'm afraid of the looks of a zombie. I read a lot of stack overflow posts on this, but I still can't figure it out.

For simplicity, I set up two views (without data) that I need to switch. What i have done so far:

  • object creation
    // define application object
    var app = {
      vent: {},
      templates: {},
      views: {},
      routers: {},
    };

    // instantiate event aggregator and attach it to app
    app.vent = _.extend ({}, Backbone.Events);

  1. defining two very simple templates (stored in the app.templates files): the first one has some dummy text and a button (c and the identifier "test-begin"), the second only dummy text

  2. definition of 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;
        },
    });

    //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());
        },
    };

  1. mainApp ( )
    //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

});
  1. Backbone

    //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 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 subviews
                _(this._subViews).each(function(child){

                    //invoke subview close method
                    child.close();
                });
            }
        };

, , ( : ), , .log( ). ( " ", .

? .

+4
1

, - , ,

A Zombie View - , DOM, - , .

DOM , HTML . Backbone.Event , ... , "", AJAX . Backbone stopListening listenTo, .


Zombie View, .

console.log, ( still:alive) .

, :

mainApp.viewsManager.rederView(new app.views.test());

new app.views.test() , , .

, console.log.

//baseRouter controller
var baseController = {

    instructions: function() {
       mainApp.viewsManager.rederView(app.views.instructions);

    },

    beginTest: function(options) {
       mainApp.viewsManager.rederView(app.views.test);
    },
};

rederView

rederView : function(ViewClass, 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 = new ViewClass();

    //render next view
    $(rootEl).html(this.currentView.render().el);
},

close, - console.log.

//unbind any model and collection events that the view is bound to
this.stopListening(); 


100 , 1 DOM. change. view <button> , , , 100 ... 100 AJAX!

100 , this.stopListening(), , . , , , , .

var TestView = Backbone.View.extend({
  tagName: 'h1',
  initialize: function(options) {
    this.i = options.i;
    this.listenTo(options.model, 'change', function(model) {
        model.fetch();
    });
  },
  events: {
    'click button': function() {
      this.model.set("show_zombies", Date.now());
    }
  },
  render: function() {
    this.$el.append("<button>Click To Test for Zombies!</button>");
    return this;
  },
  close: function() {
    this.$el.empty(); // empty view html
    // this.$el.off(); // // Whoops! Forgot to unbind Event listeners! (this view won't get garbage collected)
    // this.stopListening() // Whoops! Forgot to unbind Backbone.Event listeners.
  }
});

var model = new (Backbone.Model.extend({
    fetch: function() {
      document.body.innerHTML += "MODEL.FETCH CALLED<br />"
    }
}));

var v;
for (var i = 1; i < 101; i++) {
  if (v) v.close();
  v = new TestView({
    'i': i,
    'model': model
  }).render();

  $('body').html(v.el);
}
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone.js"></script>
Hide result
+6

All Articles