4

My view should be destroyed after the current route position is left.

So in this schematic example the login view should be destroyed after the user entered his credentials:

Routes

I tried to solve this by using Backbone.Router events:

var Router = Backbone.Router.extend({
    initialize: function () {
        Backbone.history.start();
    },
    routes: {
        "sample" : "sample"
    },
    sample: function(){
      // Build view
      var demoView = $("<div/>")
          .appendTo(document.body)  
          .text("I am lost!");

      // Destroy view
      this.once('route', function(){
        demoView.remove(); 
      });
    },
});

Unfortunately this does not work as the route events are raised after the routes are executed:

http://jsfiddle.net/hcuX9/

Is there a solution to destroy views after leaving the route position?

Do I have to hack a new event into Backbone.js?

jantimon
  • 36,840
  • 23
  • 122
  • 185

3 Answers3

3

What I use to do is to have an App.current variable pointing to the current view being rendered.

At the top of each route (or the relevant ones in your case), I remove the current view from App.current and then assign it the new view:

someRoute: function() {
  if(App.current && App.current.remove) App.current.remove();  

  // Asign a new current page
  App.current = new SomeView();
  ...
}

That way I only let one view live per route, getting rid of problems like yours.

If you don't like to be checking for App.current and invoking the remove method at the top of every route, you can listen for Backbone.history route event and injecting that logic there:

Backbone.history.on('route', function() {
  if(App.current && App.current.remove) App.current.remove();  
});
jviotti
  • 17,881
  • 26
  • 89
  • 148
1

I think you are stuck with your hack, unless you can adapt .listenTo to your needs - then you will need to fire a custom event with .trigger anywhere you have a route change, which might not be possible. Note that this functionality has been requested (and denied) before in backbone:

https://github.com/documentcloud/backbone/pull/494

See that pull request for other patches that try to do the same thing you are doing.

Exocentric
  • 98
  • 1
  • 4
0

Here, we're using on and off to listen for route events coming in instead of once because we can't rely on a single event not being the current route. When we receive a route even that is not our current route, we can destroy the view and remove the listener:

  // Destroy view
  var self = this;
  var onRoute = function(route, params){
    if(route !== 'sample'){
      demoView.remove();
      self.off('route', onRoute);
    }
  };
  this.on('route', onRoute);

I've modified your test fiddle here: http://jsfiddle.net/rgthree/hcuX9/3/


Another option, as your fiddle (not in your question) navigates directly to another view. This causes the other route's event to fire after the sample2 route. Because of this the above will remove the view. Now, it's much more complete. A hackier way you could handle it is to simply defer the once in a setTimeout so it doesn't listen until after the current route has been fired:

// Destroy view
var self = this;
setTimeout(function(){        
  self.once('route', function(){
    demoView.remove(); 
  });
}, 0);

You can see your fiddle with this method here: http://jsfiddle.net/rgthree/hcuX9/4/

rgthree
  • 7,217
  • 17
  • 21
  • I don't see where this differs from my code. Could you please provide a fiddle? – jantimon Mar 30 '13 at 00:30
  • What's with the downvote? This is the correct answer. My code differs from yours because you're using `once`, which fires only one time. Unfortunately, that one fire removes the view _and_ the listener at the wrong time. What you want to do is only remove the view and listener when another route that is _not_ the current, here "sample." Thus, you need to control the listener using `on` and `off`. I'll add the modified version of your test fiddle: http://jsfiddle.net/rgthree/hcuX9/3/ – rgthree Mar 31 '13 at 18:16
  • You are correct. You know why, right? It is because the first route's route event fires after the the second route event, thus, it is different. You can easily fix that.. but your question didn't ask for redirecting, so, I did anser your question 100% correctly... – rgthree Apr 01 '13 at 15:40
  • I was looking for a proper way to destroy my view after the route was changed. Your solution does not work in all cases. – jantimon Apr 01 '13 at 15:44
  • I get it. A hacky way is to just defer the `once` method in a `setTimeout` so it doesn't start listening until after the current route has fired. You can see it here: http://jsfiddle.net/rgthree/hcuX9/4/ – rgthree Apr 01 '13 at 15:48