Build a Currency Converter with jQuery Mobile and Cordova: 5
In the previous article in this series, I described the classes and functions needed to perform currency conversions within our app. I also illustrated the Cordova Globalization API and how we can use it to translate the written instructions on the main page of “Currency Converter.” In this next installment, I’ll show you the remaining functions of the functions.js
file and a brief overview of the Cordova Connection and InAppBrowser APIs.
Utility Functions
Updating the “Last Update” Label
In the introductory article of this series, I explained that our project will store the downloaded currency rates so that the user can use the application offline. Since the currency exchange rates change every day, it’s important to notify the user of how old his exchange rates are. If he feels that they are too old, he can run an update and download the latest. For this reason, the main page has the following code:
<label id="last-update-label">Last update of exchange rates:</label>
<span id="last-update"></span>
The <span>
having id="last-update"
is where we’ll inject the date of the last update. Th date will be written in the format that best suits the user preferences determined by the Globalization API, which I explained in the previous article. Recalling the third article where we talked about the user settings, you should recall that we update and save the date each time the user request an exchange rates update via the Settings
class and its methods.
The function that updates the text of the above <span>
tag is updateLastUpdate(),
and its markup is the following:
function updateLastUpdate()
{
if (typeof Settings.getSettings().lastUpdate === 'undefined')
{
$('#last-update').text('-');
return;
}
// Show the last time the rates have been updated
navigator.globalization.dateToString(
new Date(Settings.getSettings().lastUpdate),
function (date)
{
$('#last-update').text(date.value);
},
function ()
{
$('#last-update').text('-');
}
);
}
Updating the Rates
The method that deals with the actual updating is updateExchangeRates()
. At the very beginning of the method, there is a test to verify whether the device is connected to the Internet or not. The test is done using the Cordova Connection API—an object that gives access to the device’s cellular and wifi connection information. It has one property called type
that checks the active network connection that is being used and can assume the following values (as constants):
- Connection.UNKNOWN
- Connection.ETHERNET
- Connection.WIFI
- Connection.CELL_2G
- Connection.CELL_3G
- Connection.CELL_4G
- Connection.NONE
If the device has an active connection, the function makes a call to the European Central Bank RSS feed to retrieve the rates using the jQuery ajax()
method. Once retrieved, the rates are stored using the Currency
class, and the lastUpdate
data of the user’s settings is updated as well, so the user knows that they’re working with the latest currency exchange rates.
The two select boxes and the “last update” label of the main page are updated using this current data. Then, the two select menus are filled using a function called fillCurrenciesSelection()
, which I’ll describe in a few moments. As a final note, if the rate update fails, the user is notified with an alert of this failure shown using the Cordova Notification API.
So, what exactly will happen if the first test fails? Well, if the device does not have an active Internet connection, the app will check if there are any stored rates. If there are no stored currency exchange rates, the user is notified of the issue, and the “Convert” button is disabled, because there aren’t any ways to run any currency conversions.
The code that implements this functionality is listed below:
/**
* Update the exchange rates using the ECB web service
*/
function updateExchangeRates()
{
if (navigator.network.connection.type !== Connection.NONE)
{
$.mobile.loading(
'show',
{
text: 'Updating rates...',
textVisible: true
}
);
$.get(
'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml',
null,
function(data)
{
var $currenciesElements = $(data).find('Cube[currency]');
// The EURO is the default currency, so it isn't in the retrieved data
var currencies = [new Currency('EUR', '1')];
var i;
for(i = 0; i < $currenciesElements.length; i++)
{
currencies.push(
new Currency(
$($currenciesElements[i]).attr('currency'),
$($currenciesElements[i]).attr('rate')
)
);
}
currencies.sort(Currency.compare);
// Store the data
for(i = 0; i < currencies.length; i++)
currencies[i].save();
// Update settings
var settings = Settings.getSettings();
if ($.isEmptyObject(settings))
settings = new Settings();
settings.lastUpdate = new Date();
settings.save();
fillCurrenciesSelection();
updateLastUpdate();
$('#submit-button').button('enable');
},
'XML'
)
.error(function() {
console.log('Unable to retrieve exchange rates from the provider.');
navigator.notification.alert(
'Unable to retrieve exchange rates from the provider.',
function(){},
'Error'
);
if (Currency.getCurrencies().length === 0)
$('#submit-button').button('disable');
})
.complete(function() {
$.mobile.loading('hide');
});
}
// Check if there are data into the local storage
else if (Currency.getCurrencies().length === 0)
{
console.log('The connection is off and there aren't rates previously stored.');
navigator.notification.alert(
'Your device has the connection disabled and there aren't rates previously stored.n' +
'Please turn on your connection.',
function(){},
'Error'
);
$('#submit-button').button('disable');
}
}
Filling the Select Boxes
The function to update the select boxes isn’t very hard to grasp. It simply retrieves the stored currencies using the getCurrencies()
method of the Currency
class and then insert each of them using the jQuery append()
method. It’s worth noting that after all the currencies are inserted, this function searches for the last origin and destination currencies saved within the user preferences If these last used currencies are found, they are automatically selected. In this scenario, the listview widget needs to be refreshed, which accomplished using the method selectmenu()
and passing the string refresh
to it.
The complete source of fillCurrenciesSelection()
is listed below:
/**
* Use the stored currencies to update the selection lists
*/
function fillCurrenciesSelection()
{
var currencies = Currency.getCurrencies();
var $fromCurrencyType = $('#from-type');
var $toCurrencyType = $('#to-type');
// Empty elements
$fromCurrencyType.empty();
$toCurrencyType.empty();
// Load all the stored currencies
for(var i = 0; i < currencies.length; i++)
{
$fromCurrencyType.append('
' + currencies[i].abbreviation + '
');
$toCurrencyType.append('
' + currencies[i].abbreviation + '
');
}
// Update the selected option using the last currencies used
var settings = Settings.getSettings();
if (!$.isEmptyObject(settings))
{
var currency = $fromCurrencyType.find('[value="' + settings.fromCurrency + '"]');
if (currency !== null)
$(currency).attr('selected', 'selected');
currency = $toCurrencyType.find('[value="' + settings.toCurrency + '"]');
if (currency !== null)
$(currency).attr('selected', 'selected');
}
$fromCurrencyType.selectmenu('refresh');
$toCurrencyType.selectmenu('refresh');
}
Managing External Links
As you’ve seen in the interface design of “Currency Converter”, the aurelio.html
file have several external links within it. Those links aren’t very useful, but I do need to explain an important concept.
Until a few versions ago, Cordova opened external links in the same Cordova WebView that was running the application. So, once it opened a link, when the user clicked the “back” button, the last project’s page was shown exactly as it was before the user left it. But, in the most recent release of the framework, this behavior was changed; now, the external links are opened by default using the Cordova WebView if the URL is in your app’s whitelist. URLs that aren’t on your whitelist are opened using the InAppBrowser API.
Citing the official documentation, the InAppBrowser is a web-browser that is shown in your app when you use the window.open call.
This API has three methods: addEventListener()
, removeEventListener()
, and close()
. The first allows you to listen for three events (loadstart
, loadstop
, and exit)
and allows you to attach a function that runs as soon as those events are fired. The second method, as you might guess, is used to remove a previously-attached listener. Finally, the close()
method is used to close the InAppBrowser window.
If the user opens a link that is shown in the InAppBrowser or the system browser and then goes “back” to the application, all the CSS and jQuery Mobile enhancements are lost. This happens because the external link doesn’t the reference the cited files and when the user goes back into the app; the file is loaded as it were requested for the first time, but without passing through the main page. Obviously, this is something you’ll want to avoid, which highlights the importance of the the whitelist within the Cordova configuration file and the next function come in help.
The aim of the openLinksInApp()
function is to catch the clicks on all the external links (recognized by using the target="_blank"
attribute), preventing the unwanted default behavior and opening them using the window.open()
method. As you’ll see in the code shown below, I’ll use the _target
parameter so that the links will be opened using the WebView, because I’ll put these URLs in the Cordova whitelist. Using this technique, we’ll avoid the issues described above, and your app will continue to look and function as expected.
/**
* Open all the links as internals
*/
function openLinksInApp()
{
$("a[target="_blank"]").on('click', function(event) {
event.preventDefault();
window.open($(this).attr('href'), '_target');
});
}
Conclusion
In this article, I described several other functions of the functions.js
file, highlighting the key points of each. I also explained the Cordova Connection and the InAppBrowser API and how they work. As we’ve seen, dealing with external links is extremely important when you’re using jQuery Mobile to build your mobile app interface. In the next and final article in this series, I’ll show you the function that initializes the application, called initApplication()
, and the Cordova configuration file that we will use alongside the Adobe PhoneGap Build service to package our “Currency Converter” app.