Power of Eloquence

When saying “Hello World!” isn’t enough anymore

Getting Down With PhoneGap/Cordova(Part 2)

| Comments

And now we’re on the second part of the building mobile apps.

In this part 2 of the series, here I will guide you to why we need persistent storage and which storage technologies should we oughta used for making storing data.

Persistent storage is simply a mean way to keep the state of the data onto computing hardware device, throughout its operating life, without worrying about how it could be become obsolete or corrupted during its stages to be saved upon the device.

When build typical CRUD apps, it’s atypical to think data votality is a must consideration when designing reliable storage capacity that maintains its state.

In this example, since we’re using Javascript-based mobile framework to build our Bizdirectory app, I have the following front-end stack options we could utilise

A) WebSQL

WebSQL is a web page APi for storing database into the browser calling its very own SQL database APIs, which is basically the SQLite database. This gives the developers a bit of flavour how you can neatly write SQL query functions as you would with any traditional relational databases such MS SQL Server or Oracle 10g databases.

The key here is that many common web browser vendors are offering the developers ability to performing data operations within their respective browsers such that you wouldn’t need to think of using backend-service at all. It’s a convenience for front-end to whip up couple of quick sql code on the fly without worrying too much about what’s foreign key care going to be.

Here’s a sample of the code for this app:

To start off, in the your js/services/websql/CompanyService.js, you have the following to start with this Browser API.

1
2
3
4
5
var CompanyService = function(){

   this.db = window.openDatabase("CompanyDemoDB", "1.0", "Company Demo DB", 200000);

}

With this core API, we requested browser to create and instantiate a database with four arguments; a database name, a version number, a text description, an estimated database size. Just like you would do with another normal traditional RDBMS systems to make this simple connectivity.

Once you’ve opened this database, we can now begin to perform some database transactions by doing this

1
2
3
4
this.db.transaction(function(tx){
    createTable(tx);
    setupData(tx);
})

Here, we use normal SQLite transaction API to create new tables and setting data up for createTable and setupData functions as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
var createTable = function (tx) {

    tx.executeSql('DROP TABLE IF EXISTS company');

    var sql = "CREATE TABLE IF NOT EXISTS company ( " +
        "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
        "businessName VARCHAR(255)"
        "firstName VARCHAR(50), " +
        "lastName VARCHAR(50), " +
        "title VARCHAR(50), " +
        "cellPhone VARCHAR(50), " +
        "officePhone VARCHAR(50), " +
        "email VARCHAR(50))" +
        "city VARCHAR(50), " +
        "pic VARCHAR(50), " +
        "isFavourite integer";

    tx.executeSql(sql, null,
        function () {
            console.log('Create table succeeded');
        },
        function (tx, error) {
            alert('Create table failed: ' + error.message);
        }
    );
}

........................
........................

var setupData = function(tx){

        var employees = [
            {"id": 100, "businessName": "Company A", "firstName": "James" "lastName": "King", "title":"Professional Hairdresser", "cellPhone":"0401 566 401", "officePhone":"02 80406260", "email":"someone@career.com", "city":"Sydney, NSW", "pic":"some_pic.jpg","isFavourite": 0},
            {"id": 101, "businessName": "Company B", "firstName": "Mary" "lastName": "Riviera", "title":"Professional Dancer", "cellPhone":"0401 566 402", "officePhone":"02 80406261", "email":"someone@career.com", "city":"Sydney, NSW", "pic":"some_pic.jpg","isFavourite": 0},
            {"id": 102, "businessName": "Company C", "firstName": "Harry" "lastName": "Lee", "title":"Professional Artist", "cellPhone":"0401 566 403", "officePhone":"02 80406262", "email":"someone@career.com", "city":"Sydney, NSW", "pic":"some_pic.jpg","isFavourite": 0},
            {"id": 103, "businessName": "Company D", "firstName": "Kyumin" "lastName": "Heung", "title":"Professional Footballer", "cellPhone":"0401 566 404", "officePhone":"02 80406263", "email":"someone@career.com", "city":"Sydney, NSW", "pic":"some_pic.jpg","isFavourite": 0},
            {"id": 104, "businessName": "Company E", "firstName": "Sook" "lastName": "Baedun", "title":"Professional Fashionista", "cellPhone":"0401 566 405", "officePhone":"02 80406264", "email":"someone@career.com", "city":"Sydney, NSW", "pic":"some_pic.jpg","isFavourite": 0},
            {"id": 105, "businessName": "Company F", "firstName": "Charlie" "lastName": "Cheung", "title":"Professional Gambler", "cellPhone":"0401 566 406", "officePhone":"02 80406265", "email":"someone@career.com", "city":"Sydney, NSW", "pic":"some_pic.jpg","isFavourite": 0},
        ];
        var l = employees.length;
        var sql = "INSERT OR REPLACE INTO employee " +
            "(id, businessName, firstName, lastName, title, cellPhone, officePhone, email, city, pic, isFavourite) " +
            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
        var c;
        for (var i = 0; i < l; i++) {
            c = companies[i];
            tx.executeSql(sql, [c.id, c.businessName, c.lastName, c.title, c.cellPhone, c.officePhone, c.email, c.city, c.pic, c.isFavourite],
                function () {
                    console.log('INSERT was successful');
                },
                function (tx, error) {
                    alert('Encountered error when INSERTING: ' + error.message);
                });
        }
}

If you look at these and found yourself very familiar on how traditional DBMS systems do work (ie MS SQL or Oracle apps), then you will find these are no-brainer to you. They’re just typical SQL execute statements to write. But from the client-end side where the database is stored in the browser. To start with this, in createTable section, we run executeSQL API to create a company table, if there isn’t one already. Then in our company table, we define our company fields we’re interested in by building a concatenated strings of fields. If everything looks syntactically okay, we execute the SQL string statement to create table. Next section of code in the setupData function, we write SQL insert statements to insert our data.

Pretty straight forward.

B) In-Memory

Second choice we can use is In-Memory of the browser itself and we use plain Javascript array objects to store the data, and manipulate them using plain array-like functions to deal with data.

Thus we can write plainly like so.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var CommpanyService = function(){

  this.initialize = function(){
      // no initialization needed here.
  }

  this.findById = function(id){
    var company = null;
    var l = companies.length;
    for (var i=0; i < l; i++) {
        if (companies[i].id === id) {
            company = companies[i];
            break;
        }
    }
  }

  this.findByName = function(searchKey){
    var results = companies.filter(function(element) {
      var fullName = element.firstName + " " + element.lastName;
      return fullName.toLowerCase().indexOf(searchKey.toLowerCase()) > -1;
    });
  }

  //more array-like functions to perform here
  .....
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var companies = [
       {
          "id":100,
          "businessName":"Company A",
          "firstName":"James",
          "lastName":"King",
          "title":"Professional Hairdresser",
          "cellPhone":"0401 566 401",
          "officePhone":"02 80406260",
          "email":"someone@career.com",
          "city":"Sydney, NSW",
          "pic":"some_pic.jpg",
          "isFavourte": 0
       },
       {
          "id":101,
          "businessName":"Company B",
          "firstName":"Mary",
          "lastName":"Riviera",
          "title":"Professional Dancer",
          "cellPhone":"0401 566 402",
          "officePhone":"781-000-0002",
          "email":"someone@career.com",
          "city":"Sydney, NSW",
          "pic":"some_pic.jpg",
          "isFavourte": 0
       },
       {
          "id":102,
          "businessName":"Company C",
          "firstName":"Harry",
          "lastName":"Lee",
          "title":"Professional Artist",
          "cellPhone":"0401 566 403",
          "officePhone":"781-000-0003",
          "email":"someone@career.com",
          "city":"Sydney, NSW",
          "pic":"some_pic.jpg",
          "isFavourte": 0
       },
       //..... the rest of the data goes here
    ];

That’s it! Again, pretty straight forward to implement.

You can more simple add Javascript functions to this service class as you wish. For a simple app, I just want to have the ability to add, modify, search and favourite some of my companies that I like to approach for service.

C) Local Storage

Third choice is we use the HTML5’s LocalStorage API for storing and caching data inside the client’s browser. The most common data type for storing in the browser is atypically JSON so I have the companies JSON feed file, which is locally stored (or can remotely fetched from external server for eg API), and you can use JQuery’s AJAX calls to fetch it and store inside the browser. The following code does this.

1
2
3
4
5
6
7
8
9
10
11
12
13
var CompanyService = function () {

    this.initialize = function() {
        var deferred = $.Deferred();

        // Store sample data in Local Storage
        $.getJSON("data/companies.json").done(function(data){
            localStorage.setItem("companies", JSON.stringify(data));
        });
        deferred.resolve();
        return deferred.promise();
    }
}

Very simple. I’m making JQuery’s getJSON function call to fetch my locally stored data companies JSON feed via normal GET request method. Upon its successful completion of this data fetch (using Promises), we make anonymous callback function to serialize JSON feed, then store it inside client’s browser and assign it with unique key name, which in this case is companies. Then you’re done!

On one note though. As you’re browsing the json feed data locally without a webserver, depending on which browsers you’re viewing the app from, you may encountered cross origin request errors. These errors suggest security implications that any locally static data viewed in the browser via non-HTTP protocol will be denied access. Because we’re viewing the app statically in the browser, not on an actual webserver, it uses file protocol by default.

Webkit browsers like Chrome do not permit such as file access thus you cannot make such ajax calls to fetch, while other browsers like Mozilla Firefox are bit more ‘relaxed’. Thus you may want to use such those to test and develop your app further. However, (if you’re like me) if Chrome is your prefered web browser choice of development, to workaround this cross-origin request issue, you can append the following command line switch parameters when opening up Chrome.

1
--allow-file-access-from-files

Doing this will tell Chrome to permit any data that’s served via file protocol can be accessed in the browser. Thus I’d find it’s a quick and win way to find out your AJAX functions works as expected before you decide the next stage to run webserver that serves these static files later on.

D) API

Lastly, we have our very own an API web server, which either you write one to build a web server that will statically serve your end points to fetch/post any data or you sign up with third-party API vendor that caters these web services.

Thus our service code (hypothetically) looks like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var CompanyService = function() {

  var url;

  this.initialize = function(serviceURL){

    url = serviceURL || 'http://localhost:5000/companies'; // either a remote API url or your own statically hosted web server.

    var deferred = $.Deferred();
        deferred.resolve();
        return deferred.promise();
  },

  this.findById = function(id){
    return $.ajax({
      url: url + "/api/id",
      type: "GET",
      data: {
        id: id
      }
    });
  },

  this.findByName = function(SearchKey){
    return $.ajax({
      url: url + "/api/name",
      type: "GET",
      data: {
        name: name
      }
    });
  }
}

As you do with any remote API calls, we always use a lot of AJAX/Promises calls for various data operations.

For the purposes of this tutorial, I will be focusing on the LocalStorage example as the main scope for this tutorial development. I think not only it’s one of the easier concepts to grasp when building client-side browser apps for mobile development, but these are the common technologies JS developers are facing everyday when implementing their CRUD applications. I will look into the API server as a separate tutorial in the future.

On the third part of these tutorial series, I will show you how to build the UI for business directory app step-by-step.

Comments