Removing view and model objects in Backbone.js

What is the most efficient way to handle instances of a model / view when they are not needed?

Usually I put all the logic in the controller / router. This is the one that decides which opinions should be created, and which models should be provided to them. Typically, there are several handler functions that correspond to different user actions or routes, where I create new instances of views every time the handler executes. Of course, this should eliminate everything that I previously stored in the view instance. However, there are some situations where DOM event handlers are stored in some views and they do not get the bindings appropriately, which causes these instances to survive. I wish there was a correct way to destroy view objects when, for example, their el (DOM view) is detached or thrown out of the DOM

+63
javascript
Sep 11 '11 at 15:30
source share
3 answers

you are on the right track. you must have an object that controls the life cycle of your views. I do not like to do this in my opinion. I like to create a separate object for this.

what you need to do is disable events when necessary. To do this, it is recommended that you create a "closed" method for all your views and use an object that controls the life cycle of everything to always call the close method.

eg:

function AppController(){ this.showView = function (view){ if (this.currentView){ this.currentView.close(); } this.currentView = view; this.currentView.render(); $("#someElement").html(this.currentView.el); } } 

you must configure your code for only one instance of AppController, and you always call appController.showView(...) from your router or any other code that should show the view in the #someElement part of your screen.

(I have another example of a very simple basic application that uses "AppView" (the basic view that launches the main part of the application): http://jsfiddle.net/derickbailey/dHrXv/9/ )

The close method does not exist by default, so you need to create it yourself for each of your views. There are two things that should always be in the close method: this.unbind() and this.remove() . in addition to these, if you attach your opinion to any model or collection events, you must untie them with the close method.

eg:

 MyView = Backbone.View.extend({ initialize: function(){ this.model.bind("change", this.modelChanged, this); }, modelChanged: function(){ // ... do stuff here }, close: function(){ this.remove(); this.unbind(); this.model.unbind("change", this.modelChanged); } }); 

this correctly clears all events from the DOM (via this.remove() ), all events that can be displayed by the view itself (via this.unbind() ), and the event associated with viewing the model (via this.model.unbind(...) ).

+78
Sep 12 '11 at 3:28
source share
— -

The simpiler method is to add a custom method to close the Backbone.View object

 Backbone.View.prototype.close = function () { this.$el.empty(); this.unbind(); }; 

Using the code above, you can do the following

 var myView = new MyView(); myView.close(); 

easy peasy.

+12
Feb 24 '13 at 22:54
source share

I always keep my eyes and sometimes use models. Making sure your looks are free may be a pain if you keep the models around. Models can store a reference to a view if they are not unrelated.

As with Backbone ~ 0.9.9, binding models to view.listenTo (), rather than model.on (), makes it easier to clear through inverse controls (views control bindings, unlike models). If view.listenTo () is used for bind, then calling view.stopListening () or view.remove () will remove all the bindings. Like calling model.off (null, null, this).

I like to clear views by expanding the view with the close function, which calls sub-views semi-automatically. Views must specify parent properties, or they must be added to the array inside the parent element called childViews [].

Here is the close function that I use.

 // fired by the router, signals the destruct event within top view and // recursively collapses all the sub-views that are stored as properties Backbone.View.prototype.close = function () { // calls views closing event handler first, if implemented (optional) if (this.closing) { this.closing(); // this for custom cleanup purposes } // first loop through childViews[] if defined, in collection views // populate an array property ie this.childViews[] = new ControlViews() if (this.childViews) { _.each(this.childViews, function (child) { child.close(); }); } // close all child views that are referenced by property, in model views // add a property for reference ie this.toolbar = new ToolbarView(); for (var prop in this) { if (this[prop] instanceof Backbone.View) { this[prop].close(); } } this.unbind(); this.remove(); // available in Backbone 0.9.9 + when using view.listenTo, // removes model and collection bindings // this.stopListening(); // its automatically called by remove() // remove any model bindings to this view // (pre Backbone 0.9.9 or if using model.on to bind events) // if (this.model) { // this.model.off(null, null, this); // } // remove and collection bindings to this view // (pre Backbone 0.9.9 or if using collection.on to bind events) // if (this.collection) { // this.collection.off(null, null, this); // } } 

Then the view is declared as follows.

 views.TeamView = Backbone.View.extend({ initialize: function () { // instantiate this array to ensure sub-view destruction on close() this.childViews = []; this.listenTo(this.collection, "add", this.add); this.listenTo(this.collection, "reset", this.reset); // storing sub-view as a property will ensure destruction on close() this.editView = new views.EditView({ model: this.model.edits }); $('#edit', this.el).html(this.editView.render().el); }, add: function (member) { var memberView = new views.MemberView({ model: member }); this.childViews.push(memberView); // add child to array var item = memberView.render().el; this.$el.append(item); }, reset: function () { // manually purge child views upon reset _.each(this.childViews, function (child) { child.close(); }); this.childViews = []; }, // render is called externally and should handle case where collection // was already populated, as is the case if it is recycled render: function () { this.$el.empty(); _.each(this.collection.models, function (member) { this.add(member); }, this); return this; } // fired by a prototype extension closing: function () { // handle other unbinding needs, here } }); 
+6
Sep 29 '13 at 4:52
source share



All Articles