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 } });