javascript - Again on Backbone Zombie Views -
i trying understand backbone , struggling zombie views. have read many stack overflow posts on matter still cannot figure out.
for sake of simplicity, set 2 views (without data) need switch. did far was:
creating object //define application object var app = { vent: {}, templates: {}, views: {}, routers: {}, }; //instantiate event aggregator , attach app app.vent = _.extend({}, backbone.events);defining 2 simple templates (stored app.templates): first 1 has dummy text , button (with , id of 'test-begin'), sec 1 dummy text
defining 2 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 view _.bindall(this, 'render', 'testbegin', 'stillalive', 'beforeclose'); //listen app.vent event this.listento(app.vent, 'still:alive', this.stillalive); }, //bind events dom elements events: { 'click #test-begin' : 'testbegin', }, //render view render: function() { this.$el.html(this.template()); homecoming this; }, //begin test testbegin: function() { backbone.history.navigate('begin', {trigger: true}); }, //still live stillalive: function() { console.log('i still alive'); }, //before closing beforeclose: function() { //stop listening 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 , see if removed view responds app.vent.trigger('still:alive'); //bind relevant fucntions view _.bindall(this, 'render'); }, //render view render: function() { this.$el.html(this.template()); homecoming this; }, }); defining router //base router app.routers.baserouter = backbone.router.extend({ //routes routes: { '': "instructions", 'begin': "begintest" }, //functions (belong 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 view-switcher) //define mainapplication object mainapp = {}; //manages views switching mainapp.viewsmanager = { //rootel rootel: '#test-container', //close current view , show next 1 rederview : function(view, rootel) { //if dom el isn't passed, set default rootel rootel = rootel || this.rootel; //close current view if (this.currentview) this.currentview.close(); //store reference 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 , attach app mainapp.baserouter = new app.routers.baserouter(); //start backbone history backbone.history.start({silent: true }); adding close function view via backbone prototype //add function backbone view prototype (available in views) backbone.view.prototype.close = function () { //call view beforeclose function if defined in view if (this.beforeclose) this.beforeclose(); //this.el removed dom & dom element's events cleaned this.remove(); //unbind model , collection events view bound 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 check zombie views, sec view triggers , event (still:alive) first view hear , respond via message sent console.log (although shouldn't). first view hear such message (in console log read 'i still alive) when has been replaced sec view.
can help me? give thanks very.
long post, if have questions, please ask
a zombie view view not in dom, listens , reacts events -- behavior expected, not typically.
if dom event handlers view not removed, view , it's in-memory html fragments not garbage collected. if backbone.event handlers not unbound properly, have sorts of bad behavior... such bunch of "zombie" view triggering ajax requests on models. problem mutual on older versions of backbone prior stoplistening
, listento
if shared models between views.
in code, don't have zombie view, because closing views.
you can see console.log
because initializing sec view (and triggering event still:alive
) before close first view.
to switch views, calling:
mainapp.viewsmanager.rederview(new app.views.test());
calling new app.views.test()
initializes sec view triggers event first listens to.
if update code following, won't see console.log
anymore.
//baserouter controller var basecontroller = { instructions: function() { mainapp.viewsmanager.rederview(app.views.instructions); }, begintest: function(options) { mainapp.viewsmanager.rederview(app.views.test); }, };
and update rederview
rederview : function(viewclass, rootel) { //if dom el isn't passed, set default rootel rootel = rootel || this.rootel; //close current view if (this.currentview) this.currentview.close(); //store reference next view this.currentview = new viewclass(); //render next view $(rootel).html(this.currentview.render().el); },
if remove line close method, have zombie view , should see console.log
.
//unbind model , collection events view bound this.stoplistening();
zombie view example in next code, creating 100 views, displaying 1 in dom. every view contains same model , listens it's change
event. when view's <button>
element clicked, updates model causes every view's model alter handler executed, calling fetch 100 times... 100 ajax requests!
the view's alter handlers called 100 times, because view close method not phone call this.stoplistening()
, when views removed page, still hear model's events. 1 time click button, model changed, , of zombie views respond, though they're not on page.
class="snippet-code-js lang-js prettyprint-override">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 test zombies!</button>"); homecoming this; }, close: function() { this.$el.empty(); // empty view html // this.$el.off(); // // whoops! forgot unbind event listeners! (this view won't garbage collected) // this.stoplistening() // whoops! forgot unbind backbone.event listeners. } }); var model = new (backbone.model.extend({ fetch: function() { document.body.innerhtml += "model.fetch called<br />" } })); var v; (var = 1; < 101; i++) { if (v) v.close(); v = new testview({ 'i': i, 'model': model }).render(); $('body').html(v.el); }
class="snippet-code-html lang-html prettyprint-override"><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>
javascript backbone.js
No comments:
Post a Comment