85

I'm currently trying to implement a destroy/remove method for views but I can't get a generic solution to work for all my views.

I was hoping there would be an event to attach to the controller, so that when a new request comes through it destroys previous views then loads the new ones.

Is there any way to do this without having to build a remove function for each view?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ad Taylor
  • 2,775
  • 5
  • 27
  • 32
  • Could you give an example of what your view eco-system is? Your question makes me think there are many views on the page at once. I can't quite visualize what you are trying to do and thus can't offer an answer that might be what you need. – Bill Eisenhauer Jul 06 '11 at 00:35
  • 1
    some other patterns from these great posts: http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/ http://coenraets.org/blog/2012/01/backbone-js-lessons-learned-and-improved-sample-app/#comment-57351 – daedelus_j May 09 '12 at 20:23

7 Answers7

163

I had to be absolutely sure the view was not just removed from DOM but also completely unbound from events.

destroy_view: function() {

    // COMPLETELY UNBIND THE VIEW
    this.undelegateEvents();

    this.$el.removeData().unbind(); 

    // Remove view from DOM
    this.remove();  
    Backbone.View.prototype.remove.call(this);

}

Seemed like overkill to me, but other approaches did not completely do the trick.

alex
  • 479,566
  • 201
  • 878
  • 984
sdailey
  • 2,030
  • 2
  • 15
  • 13
  • 10
    for what I've seen, this.remove() should call jQuery's remove, which should remove the element from the DOM but also remove data and events attached to it. So I guess the call to undelegateEvents and removeData shouldn't be necessary... Am I right? – opensas Sep 10 '12 at 22:42
  • 1
    @opensas Events were persisting past the this.remove() despite the element being removed from the DOM. this.undelegateEvents() was needed for all the events to unbind. As I said, it felt like overkill, but it did the trick. – sdailey Sep 13 '12 at 18:06
  • 3
    I like it. Eventhough you should be using `this.$el` instead of `$(this.el)` ;) – mreq Sep 21 '12 at 12:36
  • 3
    +1 for a good answer to my problem, +1 for writing your first answer :) – 1nfiniti Nov 17 '12 at 18:38
  • This answer is a life saver. Backbone should provide an easier way to remove views. This is the main cause of bugs on my backbone based code. – Wilson Freitas Mar 16 '13 at 23:00
  • Another +1 for your first answer, esp since it's a great one. Answer more! – J.J. Apr 10 '13 at 19:42
  • Where should I call the `destroy` function in my view? – Nothing Oct 26 '13 at 06:12
  • 1
    My view is not getting re-rendered on destroying and creating it again. Is it because of `this.remove()`? – Raeesaa Mar 17 '14 at 07:54
  • @user2235057 that would be the equivalent of attempting to raise someone from the dead. Create a new view the same way you created the one you destroyed, don't attempt to reuse that instance. – vikki Apr 11 '14 at 14:54
  • 1
    I am creating new view `new TripsView()` but it is not getting rendered. – Raeesaa Apr 12 '14 at 07:23
  • Thank you for this answer. Works, but I do not understand what `.removeData()` does, and was unable to locate the method in the backbone docs. – cheshireoctopus Feb 06 '15 at 21:48
  • This is cargo cult programming; if just calling this.remove() doesn't do the trick there is something else wrong in your code. – Cesar May 15 '15 at 20:16
  • nice answer, but what about Child Views, can someone put some generic bombproof code for that as well? – Alexander Mills Jun 24 '15 at 22:27
48

Without knowing all the information... You could bind a reset trigger to your model or controller:

this.bind("reset", this.updateView);

and when you want to reset the views, trigger a reset.

For your callback, do something like:

updateView: function() {
  view.remove();
  view.render();
};
joshvermaire
  • 1,476
  • 1
  • 12
  • 17
  • 5
    I don't think this is right. The View's remove function just remove's that view's element from the DOM ([see here](http://documentcloud.github.com/backbone/#View-remove)). I think this guy wants to remove the view object entirely. – Nutritioustim May 16 '12 at 13:52
  • 2
    this.remove() ends up calling jquery's remove(), which also removes data and events... Nevertheless I think you also have to call this.undelegateEvents to unbind from other events, like custom events or changes to the model.. – opensas Sep 10 '12 at 23:28
  • 21
    `this.remove()` calls `this.stopListening()` and `this.$el.remove()`. The first removes all event listeners added using `this.listenTo(...)`. The second removes all event listeners add using jQuery. Between the two, you should be covered unless you used some other means of adding event listeners. So this answer is correct and gets +1 from me. – chowey Mar 08 '13 at 23:22
20

I know I am late to the party, but hopefully this will be useful for someone else. If you are using backbone v0.9.9+, you could use, listenTo and stopListening

initialize: function () {
    this.listenTo(this.model, 'change', this.render);
    this.listenTo(this.model, 'destroy', this.remove);
}

stopListening is called automatically by remove. You can read more here and here

Bassam Mehanni
  • 14,796
  • 2
  • 33
  • 41
8

This is what I've been using. Haven't seen any issues.

destroy: function(){
  this.remove();
  this.unbind();
}
JT703
  • 1,311
  • 4
  • 17
  • 41
4

According to current Backbone documentation....

view.remove()

Removes a view and its el from the DOM, and calls stopListening to remove any bound events that the view has listenTo'd.

Dre
  • 1,985
  • 16
  • 13
0

I think this should work

destroyView : function () {
    this.$el.remove();
}
  • Have to kill listeners also with `this.stopListening()` and then `return this` for good measure – Brandon Nov 19 '15 at 15:41
0

You could use the way to solve the problem!

initialize:function(){
    this.trigger('remove-compnents-cart');
    var _this = this;
    Backbone.View.prototype.on('remove-compnents-cart',function(){
        //Backbone.View.prototype.remove;
        Backbone.View.prototype.off();
        _this.undelegateEvents();
    })
}

Another way:Create a global variable,like this:_global.routerList

initialize:function(){
    this.routerName = 'home';
    _global.routerList.push(this);
}
/*remove it in memory*/
for (var i=0;i<_global.routerList.length;i++){
    Backbone.View.prototype.remove.call(_global.routerList[i]);
}
Deot
  • 1
  • 1
  • The first approach worked for me, I ran into a similar problem with views ghosting and the events were firing multiple times when each view was being recreated when you submitted the form – ONYX Jan 08 '17 at 22:30