Thursday, May 9, 2013

Introduction to IndexedDB

Today I am going to write about another storage method which is available in HTML5 as well is in Windows Store Apps which is “IndexedDB”. IndexedDB is an API for client-side storage where significant amounts of structured data can be stored. Since IndexedDB is based on indexes, it provides high performance query capability.

IndexedDB has two API modes which are Synchronous and Asynchronous. As in to the date I am writing this post, synchronous API of IndexedDB has not yet been implemented in any browser, so I will be using the asynchronous API here. We can get the asynchronous access to a database, by calling open() on the indexedDB attribute of a window object. When using asynchronous API, database operations do not execute immediately; instead operations return request objects that are executed in the background. And again IndexedDB is an event-driven API. So we can define event handlers for returned requests and then respond to success, failure etc events of those requests.

Now lets dive into see how we can use IndexedDB in a HTML5 application. I am creating a simple web applictaion to store my ToDO list.

Before creating a IndexedDB, first thing to do is check whether the browser supports IndexedDB. As in to the date I am writing this post, these are the browsers and their versions which supports IndexedDB.
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB; 

if (!window.indexedDB) {
    alert("Your browser doesn't support IndexedDB");
}
After checking the browser support I can create the database.
function initDb() {
    var request = indexedDB.open("ToDoDB", 1);
    request.onsuccess = function (event) {
        db = request.result;
        showAllItems();
    };
    request.onerror = function (event) {
        console.log("IndexedDB error: " + event.target.errorCode);
    };
    request.onupgradeneeded = function (event) {
        var objectStore = event.currentTarget.result.createObjectStore("todo", { keyPath: "id", autoIncrement: true });
        objectStore.createIndex("priority", "priority", { unique: false });
        objectStore.createIndex("tododesc", "tododesc", { unique: true });
    };
}
Here indexedDB.open() accepts two parameters (database name and the version number) and returns a IDBOpenDBRequest. If the database is not there, the open() method will create a new one. When the database open request succeeds the request.onsuccess event is fired. If an error occurred, request.onerror event is fired. In the case where the database’s version is smaller then the provided version the request.upgradeneeded event will be fired and you will be able to change the database’s structure in it’s handler. In the handler we can create a objectStore. Object store holds records, which are key-value pairs. Records within an object store are sorted according to the keys. This sorting enables fast insertion, look-up, and ordered retrieval. Key paths and key generators are used to create the main index for the stored value. The key is like a primary key. Then I am creating another indexes to search my “todo” object store by “priority” and “tododesc”. In here I am putting a unique constraint to "tododesc", assuming I can’t have same "tododesc". But same priority can be there with many "tododesc".

Add New Item

function addNewItem() {
    var priority = "Normal"
    var tododesc = "My Task 1"

    var transaction = db.transaction(["todo"], "readwrite");
    var objectStore = transaction.objectStore("todo");
    var request = objectStore.add({ priority: priority, tododesc: tododesc });
    request.onsuccess = function (event) {
        alert("added");
};
Before doing anything with the database, I need to start a transaction first. Transactions come from the database object, and we have to specify which object stores I want the transaction to span. Also, I need to mention whether I am going to make changes to the database or I am just going to read from it. Then I am getting my particular object store from the transaction and I am adding a new object to it. Again it will return me a request which the events can be fired upon.

Delete Item

function deleteItem(id) {
    var transaction = db.transaction(["todo"], "readwrite");
    var objectStore = transaction.objectStore("todo");
    var request = objectStore.delete(parseInt(id));
    request.onsuccess = function (event) {
        alert("deleted");
    };
    request.onerror = function (event) {
        alert("error deleting record");
    };
}
Again it’s same as adding a new item. I am just parsing the id to delete.

Show All Items

function showAllItems() {
    var transaction = db.transaction(["todo"], "readwrite");
    var objectStore = transaction.objectStore("todo");
    var request = objectStore.openCursor();
 
    request.onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
            alert("ID: " + cursor.key + " \nPriority: " + cursor.value.priority + " \nTo Do Desc: " + cursor.value.tododesc + " ");
            cursor.continue();
        }
        else {
            // no more records
        }
    };
}
To get all the items in the object store, I can use a cursor. Cursors are used to iterate over multiple records in a database. The openCursor() function takes several arguments. First We can limit the range of items that are retrieved by using a key range object. Second, we can specify the direction that we want to iterate. Here it will be in the ascending order. Then to keep iterating, I am calling cursor.continue().

Delete Database

function deleteDb() {
    var iDb = indexedDB.deleteDatabase("ToDoDB");
    iDb.onsuccess = function (event) {
        alert("database deleted");
    };
    iDb.onerror = function (event) {
        alert("error deleting database");
    };
}
Deleting the database is pretty much straight forward. Just call the deleteDatabase of the indexedDB of a window object. You have to provide the database name, and the database will be deleted.

I am uploading a full sample to my SkyDrive so you can have a jump start on IndexedDB.

Happy Coding.

Regards,
Jaliya