Handling errors and internet connectivity problems in Windows 8 Apps with navigation (WinJS)
In this post we will explore a generic way of handling unpredicatble errors/lost connectivity in our Windows 8 Apps developed with HTML5 and
Javascript. The two requirements we need to tackle are as follows:
- There is a chance that while our application is running, internet connectivity will be lost. We need to know when this happens in
order to inform the user and possibly handle the problem while preserving application state.
- Unpredicatble errors may occur that will cause a general exception. We need to "catch" those errors, informing the user with a
generic (and apologetic) error message and maybe transmit some useful details about the error to a service we have implemented for this purpose.
Handling internet connectivity problems
First we need a way to verify whether the app has connectivity to the internet. This can be done by using the following function:
utilities.isConnected=function() {
var connectivity=Windows.Networking.Connectivity;
var profile = connectivity.NetworkInformation.getInternetConnectionProfile();
if (profile) {
return (profile.getNetworkConnectivityLevel() != connectivity.NetworkConnectivityLevel.internetAccess);
}
else {
return false;
}
}
The function returns true if the application is connected to the internet and false if it is not. This function can be used in a page whenever the
user clicks on a button or in general an action will be invoked that will trigger a WinJS.xhr or something similar and if it returns false the application should prevent the action from
being executed.
The same can be applied when the user navigates to a page that uses the internet in its "onload" event. But in this case what can you do? Meaning
that the user reaches the page/pages that to fetch data "on load", the connection is down so nothing is fetched and then you need to display a button or something or start a timer that
checks the coneectivity and at some time refreshes the page.
Or you could easily and with a few lines of code handle this case generally as follows: The idea is to use a "nointernet" page
and lead the user there when you detect there is no internet. Then you prompt the user to hit the back button or a button you provide when he feels that the connection has been re-
established. So let's see how this works:
First you need to implement a simple "no internet" page. A snapshot of the page is given below.
In the navigator.js file that handles the navigation, we find the _navigated function that is called whenever a user goes from one page to another
before the load event of the new page and we add the following lines at the top:
_navigated: function (args) {
if (args.detail.location.indexOf("nointernet") == -1 && !utilities.isConnected()) {
nav.navigate("/pages/nointernet/nointernet.html");
return;
}
// ...rest of the function
This means that if we are not already in the "nointernet" page and the internet connectivity has been lost instead of going wherevere the user
chose to go we navigate to the "nointernet" page.
In the "nointernet" page the click event handler of the "Retry" button calls WinJS.Navigation.back();
And this is it. With just a few lines of code you can handle with dignity all connectivity issues that may occur during the user's navigation in all
application pages
Handling unpredicatble erros
The second requirement states that the app should be able to handle unpredictable errors in a graceful manner. We will follow a similar approach
with an "error" page dedicated just for that.
The "Retry" button with just call again WinJS.Navigation.back() hoping that ther error was temporar. But how do we send the user to this page upon
an unpredictable error?
We just need to handle the onerror event in the default.js file (or in some other file) as follows:
app.onerror = function (customEventObject) {
var errorMessage = '';
var errorName = '';
if (customEventObject.detail.error) {
errorMessage = customEventObject.detail.error.message;
errorName = customEventObject.detail.error.name;
}
else {
errorMessage = customEventObject.detail.exception.message;
errorName = 'Exception';
}
var optionsObject = { errName: errorName, errMsg: errorMessage };
nav.navigate("/pages/error/error.html", optionsObject);
return true;
}
Now if you want to take this one step further and have some private service listening for those errors (so you know what your users are
experiencing) you can implement a simple ASP.MVC 3 application (empty), add just one controller (say ServicesController) with a single Action called Error:
public class ServicesController : Controller
{
public JsonResult Error(string errName,string errMsg)
{
// Store the data in the database or whatever
return Json(new {stored=true},JsonRequestBehavior.AllowGet);
}
}
You host this service in the net and then in the "error" page (on ready event) you write something similar to the following:
ready: function (element, options) {
element.querySelector("#buttonRetry").onclick = function (ev) {
WinJS.Navigation.back();
}
WinJS.xhr({ url: 'http://localhost:10162/Services/Error?errName=' +
options.errName + '&errMsg=' + options.errMsg }, function (data) {
});
}
Now you have a personal bug reporting tool so you know the problems that occur for each one of the deployed application instances out there.