Build a Currency Converter with jQuery Mobile and Cordova: 4

Share this article

In the previous article of this series, I described the jQuery Mobile custom configuration for our app and highlighted the importance of allowing cross-domain requests to download up-to-the-moment currency exchange rates. I also described a class that will help us translate the app into multiple spoken languages, and I outlined a second class to manage user settings. In this fourth article, I’ll show you the classes that save, load, and complete currency operations, as well as the one that unifies all of the different components of our app.

The Currency Class

When I talked about the Settings class, I described the aim of the _db and _tableName variables. The Currency class has these variables too, and it shares the same purpose. Of course, the value of _tableName is different, as are the “currencies.” Keep in mind that we’re not storing an object—we’re storing an array containing Currency instances. We have to store data beyond the last update or the last currencies used; we also need to save the name of the currency (actually its abbreviation) and the conversion rate itself.

We’re retrieving the rates from the European Central Bank, and they use the Euro as the reference money. So, when you parse the feed, you won’t find the Euro rate itself (a rate of 1.00 is assumed), and the rates you’ll are all relative to the Euro. This leads to a tricky method to make non-Euro currency comparisons, which I’ll explain in detail when I describe the convert() method.

Based on what I stated so far, the beginning of the Currency class is the following:

function Currency(abbreviation, rate)
{
  var _db = window.localStorage;
  var _tableName = 'currencies';

  this.abbreviation = abbreviation;
  this.rate = rate;
}

Just like the Settings class, we’ll have the save() and the load() methods. The latter is identical to the one already discussed, while the former is quietly different because we’re managing an array. Since we don’t want duplicated currencies, we need to check if a given currency is already stored before adding to the array. If the currency is already present, we’ll just update the conversion rate. Otherwise, we’ll push the whole object into the array. To achieve this goal, I’ll create a static method called getIndex(), which returns the index of the currency if it’s found or returns false otherwise. Just like you’ve seen for the Settings class, I’ll write a utility static method to retrieve the array containing Currency instances called getCurrencies().

Now that you’re aware of this tricky situation, I can show you the code of the save() and load() methods.

this.save = function()
{
 var currencyIndex = Currency.getIndex(this.abbreviation);
 var currencies = Currency.getCurrencies();

 if (currencyIndex === false)
   currencies.push(this);
 else
   currencies[currencyIndex] = this;

 _db.setItem(_tableName, JSON.stringify(currencies));
}

this.load = function()
{
 return JSON.parse(_db.getItem(_tableName));
}

And, below is the code for the two static methods:

Currency.getCurrencies = function()
{
  var currencies = new Currency().load();
  return (currencies === null) ? [] : currencies;
}

Currency.getIndex = function(abbreviation)
{
  var currencies = Currency.getCurrencies();
  for(var i = 0; i < currencies.length; i++)
  {
    if (currencies[i].abbreviation.toUpperCase() === abbreviation.toUpperCase())
      return i;
  }

  return false;
}

Now that we’re able to save and load currencies, we need a method to do mathematical currency conversions. Before showing you the code of the methods, I should highlight one thing. As I said, the feed uses the Euro as the reference currency, so you won’t store distinct conversion rates for each and every currency—this would be a waste of memory. What we’ll devise is a method to convert from Euro to other currencies for each desired currency type.

Now, suppose that you want to convert from USD to AUD, what you can do? The solution is that you first convert to Euro and then into the currency you truly need.

To achieve this goal, you need to first divide by the starting rate and then multiply by the goal currency rate. The convert() method requires three parameters: the value to convert, the starting currency, and the goal currency. This method relies on another method, called getRate(), which, using abbreviation (the abbreviation of the currency name), retrieves the appropriate conversion rate. getRate() relies on a method that I haven’t explained yet called getCurrency(). As you might imagine, it’s very similar to getCurrencies(), except that it accepts an abbreviation as a parameter and returns a Currency instance if the search succeed or null return if it failed.

The source code of these three methods is listed below:

Currency.getCurrency = function(abbreviation)
{
  var index = Currency.getIndex(abbreviation);
  return (index === false) ? null : Currency.getCurrencies()[index];
}

Currency.getRate = function(abbreviation)
{
  var currency = Currency.getCurrency(abbreviation);
  return (currency === null) ? 0 : currency.rate;
}

Currency.convert = function(value, from, to)
{
  // Round up to the 2nd decimal
  return Math.round(value / Currency.getRate(from) * Currency.getRate(to) * 100) / 100;
}

The last two methods are those that will be used during the update of the select boxes to select the currencies to sort them alphabetically. Their names are compareTo() and compare(), and their code is the following:

Currency.prototype.compareTo = function(other)
{
  return Currency.compare(this, other);
}

Currency.compare = function(currency, other)
{
  if (other == null)
    return 1;
  else if (currency == null)
    return -1;

  return currency.abbreviation.localeCompare(other.abbreviation);
}

Utility Functions

In this section, I’ll explain the utility functions contained in the functions.js file. Please note that I’ll avoid explaining updateIcons(), because it has already been discussed and provided in a previous series. So, if you want to understand what it does, you can read the section called “Update Icons Based on the Screen Size” from Build a Location-Based Mobile App With HTML5 and Javascript: Part 4.

Testing for Requirements

“Currency Converter” doesn’t have a lot of requirements. In fact, the only mandatory condition we need is the possibility to store data using the Web Storage API. While the ability to connect to the Internet is important, it’s only needed the first time the user runs the application, because he’ll need to download the latest rates from the European Central Bank RSS feed. But, after that, the user can continue to work offline with slightly outdated currency conversion rates. Obviously, without the Internet connection enabled, we won’t be able to update the rates.

To test for requirements, I’ll create a function called checkRequirements(), which will test for Web Storage API support. If the device doesn’t support it, the app notifies the user using the Cordova Notification API and returns false, so the button to update the currency conversions can be disabled. The code of this function is shown below.

function checkRequirements()
{
  if (typeof window.localStorage === 'undefined')
  {
    console.log('The database is not supported.');
    navigator.notification.alert(
      'Your device does not support the database used by this app.',
      function(){},
      'Error'
    );
    return false;
  }

  return true;
}

As you can see I also wrote a console.log() instruction to help you debug the project in case you need to. Remember, using alert() is not a best practice.

Translating Pages

To translate the elements of index.html, I’ll use the Globalization API, introduced in Cordova 2.2.0., which allows you to get information about user’s locale and timezone, thanks specifically to methods like getPreferredLanguage() (which gets the client’s current spoken language) and getLocaleName() (which retrieves the identifier code of the client’s current locale setting e.g. en_US or it_IT).

In addition, this API has methods to perform operations like converting dates and numbers into strings using the proper local format based on the user’s settings. The methods that implement these conversions are dateToString() and numberToString() respectively. Please note that like the other Cordova APIs, these methods are asynchronous, so they use success and failure callback functions.

In our project, we’ll use some of the previously-cited methods, specifically: getLocaleName(), dateToString(), and numberToString(). This API is very important, because it allows you to move the user experience one step forward compared what you could do in previous versions. Please note that currently the supported platforms are Android, BlackBerry WebWorks (OS 5.0+), iOS, and Windows Phone 8.

As you’ll see in the code below, I used the getLocaleName() method to retrieve the language identifier, which I’ll use to get the right language from within the Translation object. Once retrieved, I’ll iterate over the text strings to translate the various instructional elements. Now, you should finally understand why I used the elements’ id as properties’ name. You’ll also note the use of Audero Text Changer to change the elements’ text, which I illustrated in the first part of this series.

/**
 * Translate the main page
 */
function translateMainPage()
{
  navigator.globalization.getLocaleName(
    function(locale)
    {
      var translation = Translation[locale.value.substring(0, 2)];
      if (typeof translation === 'undefined')
        return;

      for(var key in translation)
        $('#' + key).auderoTextChanger(translation[key]);
    },
    function()
    {
      console.log('An error has occurred with the translation');
    }
  );
}

Conclusion

In this fourth article, I described the Currency class and all of its methods. As you learned, it has several utility methods that allow you to write new methods with very few lines of code. I also showed how to test for the minimum app requirements and gave a brief overview of the Globalization API, as well as the specific the methods used in our project. To help us manage the translation of interface elements, I used the Audero Text Changer plugin. In the next part of the series, I’ll show you the remaining functions of the functions.js file to complete the discussion.

Aurelio De RosaAurelio De Rosa
View Author

I'm a (full-stack) web and app developer with more than 5 years' experience programming for the web using HTML, CSS, Sass, JavaScript, and PHP. I'm an expert of JavaScript and HTML5 APIs but my interests include web security, accessibility, performance, and SEO. I'm also a regular writer for several networks, speaker, and author of the books jQuery in Action, third edition and Instant jQuery Selectors.

jQuerymobilemobile web tutorialsTutorials
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week