Nerdworks logo "The nerd shall inherit the earth."

Nerdworks Blogorama

Nerdspeak

On the HTML5 Indexed DB API - Part 2 of n
Technobabble
9/4/2011 11:48:03 PM

In the previous post in this series we covered the fundamentals of the Indexed DB specification. We continue the journey in this post and take a look at the API itself.

Building an offline note taking app

For the rest of this series of posts, we’ll try and build the client side data layer for a fictitious note taking web app. From a data model point of view it’s about as simple as it can get. The app allows users to write text notes and tag them with specific key words. Each note will have a unique identifier which will serve as its key and apart from the note text, it will be associated with a collection of tag strings. Here’s a sample note object represented in JavaScript object literal notation:

var note = {
      id: 1,
      text: "Note text.",
      tags: ["sample", "test"]
    };

We’ll build a NotesStore object that has the following interface:

var NotesStore = {
        init: function(callback) {
        },

        addNote: function(text, tags, callback) {
        },
    
        listNotes: function(callback) {
        }
    };

It should be fairly self-evident as to what each method does. All method calls execute asynchronously and where a result is to be returned to the caller, the interface accepts a reference to a callback that is to be invoked with the result. Let’s see what it takes to efficiently implement this object using an indexed database.

Testing for Indexed DB

The root object that you deal with when talking to the indexed DB API is called indexedDB. In fact you can check for the presence of this object to see whether the current browser supports indexed DB or not. Like so:

if(window[“indexedDB”] === undefined) {
      // nope, no indexed DB!
    } else {
      // yep, we’re good to go!
    }

Alternatively, you can use the Modernizr JavaScript library to test for support for indexed DB like so:

if(Modernizr.indexeddb) {
        // yep, go indexeddb!
    } else {
        // bleh! No joy!
    }

Asynchronous requests

The asynchronous API calls work through what are known as “request” objects. When an asynchronous API call is made, it would return a reference to a “request” object which exposes two events – onsuccess and onerror. The former is raised when the API executes successfully and the latter is raised when it errors out. Here’s what a typical call looks like:

var req = someAsyncCall();
    req.onsuccess = function() {
        // handle success case
    };
    req.onerror = function() {
        // handle error
    };

As you work with the indexedDB API you eventually get to a point where it becomes somewhat hard to keep track of all the callbacks. In a future post I’ll explore some ideas I am currently playing with on how we can simplify this further. For now though, to make our exploration of the API somewhat simpler I’ll define and use a small utility routine that abstracts the “request” pattern away:

var Utils = {
        errorHandler: function(cb) {
            return function(e) {
                if(cb) {
                    cb(e);
                } else {
                    throw e;
                }
            };
        },
    
        request: function (req, callback, err_callback) {
            if (callback) {
                req.onsuccess = function (e) {
                    callback(e);
                };
            }
            req.onerror = errorHandler(err_callback);
        }
    };

Now, I can write my async calls like so:

Utils.request(someAsyncCall(), function(e) {
        // handle completion of call
    });

Creating and opening the database

Creating/opening a database is done by calling the open method of the indexedDB object. Here’s an implementation of the NotesStore object’s init method:

var NotesStore = {
    name: “notes-db”,
    db: null,
    ver: “1.0”,
    init: function(callback) {
        var self = this;
        callback = callback || function () { };
        Utils.request(window.indexedDB.open(“open”, this.name), function(e) {
            self.db = e.result;
            callback();
        });
    },

    …

The open method opens the database if it already exists or creates a new one if not. The success callback of the request object returned by open receives an event object as a parameter whose result property is a reference to the newly opened database. You can think of this as the object that represents the connection to the database. When this object is destroyed (or if you called its close method) the connection to the database is terminated.

Coming up

Now that we have the database created, in the next post, we’ll go ahead and create the rest of the database objects.

 
as 9/19/2011 3:06:46 PM
Thanks for putting this together. I'm lost on one thing - in the last code sample, what does the "callback = callback || function () {};" do?

Thanks!
 
Ranju V 9/19/2011 11:27:47 PM
Thanks for your comment. The line "callback = callback || function(){}" basically allows me to call "callback" later on without having to check if a callback function was passed to "init" or not. The expression on the right hand checks if "callback" has a valid value and if it does then its basically a self-assignment - nothing changes. If "callback" is null or undefined however then a blank no-op function gets assigned to "callback". This allows me to not check whether "callback" is valid later on.
 
Alex Simons 9/21/2011 4:00:49 PM
Thanks. That makes sense!
 

Please fill this form and click on the "Submit" button to post a comment. All fields except the comment box are optional. You don't have to give me your name and email, but if you do then that might allow me to follow up with you on your comment. Also, I won't publish your email address here or anywhere else.

 
Your Name :
Your Email :
Your Comment :
   

What in your opinion do you get when you multiply the number 5 by the number 2?

Your answer will help me figure out whether you are human or a spam bot. If you're a spam bot I hope your kernel core dumps and your CPU bursts into flames.

   

Please click here to go back to the blog.

blogorama home
about this blog
email the author
where on earth am i?
subscribe to mailing list
feeds Use these links for feed syndication
rss  |  atom
by category
technobabble (54)
philosophical crud (3)
irrelevant stuff (7)
archive
july, 2011 (3)
june, 2011 (2)
may, 2011 (3)
april, 2011 (1)
march, 2011 (1)
february, 2011 (1)
february, 2010 (1)
october, 2009 (1)
september, 2009 (1)
july, 2009 (5)
march, 2009 (2)
august, 2008 (2)
march, 2008 (1)
january, 2008 (1)
september, 2007 (2)
april, 2007 (1)
february, 2007 (2)
december, 2006 (1)
october, 2006 (1)
september, 2006 (4)
august, 2006 (3)
july, 2006 (4)
june, 2006 (3)
may, 2006 (6)
april, 2006 (2)
recent entries
IE9 web cast / Chen...
Partial function ap...
Web Camps, Virtual...
On the HTML5 Indexe...
On the HTML5 Indexe...
On the HTML5 Indexe...
On the HTML5 Indexe...
On JavaScript code...
JavaScript strict m...
On JavaScript stric...
179789 hits