We have a backbone.js app that displays a number of forms to the user. What we want is very simple: if the user goes to another page without saving the filled-in form, we want to display a confirmation dialog.
In classic forms this is easy enough, just implement window.onbeforeunload (or $(window).on('beforeunload') in jQuerysh). But backbone apps only have one view, typically. I tried a bit using onHashChange, but returning false in that callback does not prevent Backbone from still going to the other view.
Pointers are appreciated. Searching the interwebs didn't find me any valid response.
I've been dealing with this issue for a little while, and I've come up with a solution. I based my solution off this example.
The idea is to override the
navigate
method, and usejQuery
deferred
objects to wait to the appropriate time to navigate. In my case, if a user tried to navigate from my view that was dirty, a dialog needed to show that asked the user if:1) Save the changes, then navigate 2) Don't save the changes, and navigate 3) Cancel the navigation and remain on the existing page
Below is my code for the navigate method in the
Router
:The
showConfirm
method presents the dialog with the three options I listed above. Based on the user's choice, I save the form, then resolve the answer to navigate, etc.I think you could hack Backbone.history.loadUrl ( http://documentcloud.github.com/backbone/docs/backbone.html#section-137 ). I did a quick test, this code does a check every time you change pages. You'll want to add code to activate the check only when there is actually a reason for it.
You'll have to handle window.onbeforeunload as well, because the user might still leave the page entirely.
Since version 1.2.0 you can override method
Router.execute
and returnfalse
to cancel routing, like this:I was getting multiple calls to
loadUrl
per reroute using Dénes's solution, so I decided to try this method, which worked for me.Use it like this:
I would avoid hacking around with Backbone. You could do this globally for all links by replacing the part where you would normally start
Backbone.history
with something likeYou need of course to replace the
changesAreSaved
with something that makes sense and add whatever other login you have about handling links.I would also hack
Backbone.history.loadUrl
, that's where loading the route callbacks happen.Explanation:
1)
Backbone listens to the
hashchange
event and setsBackbone.history.checkUrl
as a callback: https://github.com/jashkenas/backbone/blob/1.1.2/backbone.js#L14142)
Backbone.history.checkUrl
checks if the hash has changed and callsBackbone.history.loadUrl
3)
Backbone.history.loadUrl
finds the first matching route and calls its callback:Useful note:
Backbone.history.fragment
stores the current hash, it's set inBackbone.history.loadUrl
, so we can access it AFTER thehashchange
event but BEFORE the router callbacks do their job.