Monday, December 17, 2012

Destroy Backbone View

When look at backbone.js View (http://backbonejs.org/#View), it is a Javascript object for UI piece, and works very well with backbone model (can be updated independently when the model changes). However, there is no handy destroy() or cleanup() or close() method to completely get rid of backbone view.

Then how to destroy a backbone view? 
Suggestion 1:
Backbone.View.prototype.destroy = function(){
  this.remove();
  this.unbind();
  if (this.onClose){
    this.onClose(); // in each view, implement this: like this.model.unbind("change", this.render)
  }
}

Suggestion 2:
Backbone.View.prototype.destroy = function() {
  this.remove();
  this.unbind(); // alias of this.off();
  if (this.model) {
    this.model.off(null, null, this);
  }
  if (this.collection) {
    this.collection.off(null, null, this);
  }
}

Suggestion 3:
Backbone.View.prototype.destroy = function() {
    this.remove();
    this.undelegateEvents(); // use this or this.unbind/this.off?
    $(this.el).removeData().unbind(); // this is to remove custom data from jQuery data cache?
    Backbone.View.prototype.remove.call(this); // this is redundant?
}
Backbone view does provide the following APIs
1. remove() which is to remove a view from DOM
2. undelegateEvents() which is to removes all of the view's delegated events

The call to `this.remove()` delegates to jQuery behind the scenes, by calling `$(this.el).remove()`. The effect of this is 2-fold. We get the HTML that is currently populated inside of `this.el` removed from the DOM (and therefore, removed from the visual portion of the application), and we also get all of the DOM element events cleaned up for us. This means that all of the events we have in the `events: { … }` declaration of our view are cleaned up automatically!

The call to `this.unbind()` will unbind any events that our view triggers directly – that is, anytime we may have called `this.trigger(…)` from within our view, in order to have our view raise an event.

The last thing we need to do, then, is unbind any model and collection events that our view is bound to.

With that, suggestion 1 or 2 is the way to go.

Then how about sub-views?

One best practice is saving sub views in a view property, and when destroy parent view, loop through sub view list to destroy them one by one.

How to render view with sub-view?
The answer is: Use view.setElement() method. For instance,

render : function () {
    this.$el.$html(this.template());
    this.subview1.setElement(this.$('.subview-1')).render();
    this.subview2.setElement(this.$('.subview-2')).render();
    return this;
}

Backbone 0.9.9 
This 1.0 release candidate added listenTo() and stopListening() to help destroy view completely, below is from Backbone online upgrade info.

Most importantly, Backbone events have two new methods: listenTo and stopListening. These are an inversion-of-control flavor of the usual on and off, and make it a little easier to clean up all events that an object is listening to on other objects. When you destroy Views with view.remove(), this will now be done automatically. 

If when bind view to model/collection using listenTo, then our view destory will be:

Backbone.View.prototype.destroy = function(){
  this.remove(); //remove from dom, clean up dom events, and also call stopListening() to remove any bound model events that the view has listenTo'd.
  this.unbind(); //remove self triggered events
}

No comments:

Post a Comment