I struggled for awhile with how to present the 'Are you sure you want to exit?' message when a user has unsaved data and attempts to close the browser or navigate elsewhere. I also wanted pages in my application to be URL accessible so that a user could bookmark a URL to show a specific record or email a URL to another user. I am using Ext 4.1 and MVC.
I decided on a two pronged approach. First, I hooked directly into the window.beforeunload event for handling the user attempting to close the browser window or page away when they have unsaved data. It was easy to setup a handler for the beforeunload. The difficulty I ran into was finding the scope of my MVC application.
Secondly I used Ext.history to handle page navigation.
Here is an excerpt from my main MVC controller.
//-------------------------------------------------------
// function to handle the token that is passed in the URL for the page.
//-------------------------------------------------------
dispatch: function (token) {
if (token == "") { // no parameters passed, go to home page.
Ext.History.add("Main");
}
else if (token) {
parts = token.split(':');
if (parts[0] == "editCustomer") { // Edit a specified customer
this.editCustomer(parts[1]);
} else if (parts[0] == "Main") {
this.goHome();
} else if (parts[0] == "tabpanel") { // open a tab on main page
this.goHome();
var tab = this.getMainSearch().query('#tabs')[0]; //tabs
tab.setActiveTab(parts[1]); // set the main tab control to 'home'
} else {
this.goHome();
}
} else
this.goHome();
},
//-------------------------------------------------------
// Check if we need to ask if user needs to save.
//-------------------------------------------------------
processHistoryToken: function (token, opt) {
if (this.changesMade()) { // if any dirty data that is not saved.
this.token = token;
Ext.Msg.show({
title: 'Warning.',
msg: "Save changes before exiting page?" ,
buttons: Ext.Msg.YESNO,
fn: function (btn) {
if (btn == 'yes') {
// User has said yes so explicitly save the data to server.
if (this.changesMadeEvent()) // changes for my 'event' form
this.saveEvent(false);
if (this.changesMadeTicket()) // changes for my 'ticket' form
this.saveTicket(false);
}
// reset the forms so they are no longer dirty
this.getEventDetail().query('#EventDetailform')[0].getForm().reset();
this.getChangeDetail().query('#ChangeDetailform')[0].getForm().reset();
// proceed with doing the action
this.dispatch(this.token);
},
animEl: 'elId',
icon: Ext.MessageBox.QUESTION,
scope: this
})
} else {
this.dispatch(token);
}
},
//-------------------------------------------------------
// Handler for the window.onbeforeonload.
//-------------------------------------------------------
confirmExit: function () {
// Using the previously save scope, call the 'changesMade' function to see if
// any unsaved form data.
if (this.MYscope.getController('AM.controller.smMain').changesMade())
return "You will lose your changes if you leave this page !";
},
//-------------------------------------------------------
// init function for my main MVC controller
//-------------------------------------------------------
init: function () {
// -------------------------------------------------
// History management and navigation 'dispatcher' which handles some of the user navigation.
// ----------------------------------------------------
Ext.History.init();
Ext.History.hasHistory = false; // default to no history saved to prevent 'back' button completely out of the app.
Ext.History.on('change', function (token, opt) {
Ext.History.hasHistory = true; // we have some history now so 'back' button is ok.
this.processHistoryToken(token);
}, this, this);
// Store our context as a property of the window. I'm sure there are better ways to do this, but this works!
window.MYscope = this;
window.onbeforeunload = this.confirmExit;
......
// Support for URL. Set the 'page' in the application based on the URL.
var token = document.location.hash.replace("#", "");
this.processHistoryToken(token);
...