Convert WebSQL To IndexedDB Tutorial

Exactly a year ago on November 18, 2010, the W3C announced that Web SQL database is a deprecated specification. Many major browsers including Chrome, Safari, Opera and nearly all Webkit based mobile devices support WebSQL, however, if you are going to start a new project and/or you wish to have your code running with the new version of client side database (that will receive updates and improvements) you should implement indexedDB as your client side database. In this short post we will see what are the main steps to refractor your WebSQL code to IndexedDB.

First lets create the databases


// WebSQL 
database = openDatabase('todos1', '1.0', 'todo list example db', 2*1024*1024);

// InxededDB
var todoDB = {};
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
todoDB.indexedDB = {};
todoDB.indexedDB.db = null;


Create Table/ObjectStore

In both cases we need some ‘space’ to save our information


// WebSQL - creating a new table
 database.transaction(function(tx) {
     tx.executeSql("CREATE TABLE IF NOT EXISTS tasks (id REAL UNIQUE, text TEXT)", []);
   });

// IndexedDB - creating a new object store that will hold our data
todoDB.indexedDB.open = function() {
        var request = indexedDB.open("todos");

        request.onsuccess = function(e) {
          var v = "2.0 beta"; // yes! you can put strings in the version not just numbers
          todoDB.indexedDB.db = e.target.result;
          var db = todoDB.indexedDB.db;
          // We can only create Object stores in a setVersion transaction;
          if (v!= db.version) {
            var setVrequest = db.setVersion(v);

            // onsuccess is the only place we can create Object Stores
            setVrequest.onsuccess = function(e) {
              if(db.objectStoreNames.contains("todo")) {
                db.deleteObjectStore("todo");
              }
              var store = db.createObjectStore("todo",
              {keyPath: "timeStamp"});
              todoDB.indexedDB.getAllTodoItems();
            };
          }
          else {
            todoDB.indexedDB.getAllTodoItems();
          }
        };
        request.onfailure = todoDB.indexedDB.onerror;
      }

Add Item

Now it’s time to add some data to our database, no?


// WebSQL
function addTodo() {
        var todo = document.getElementById("todo");
        var task = {
          "id": new Date().getTime(),
          "text": todo.value };
        
        database.transaction(function(tx) {
          tx.executeSql('INSERT INTO tasks (id, text) values (?, ?)', [task.id, task.text]);
        });
        // now let clean it to the next todo
        todo.value = "";
        showAll();
      }
      
// IndexedDB
todoDB.indexedDB.addTodo = function(todoText) {
        var db = todoDB.indexedDB.db;
        var trans = db.transaction(['todo'], IDBTransaction.READ_WRITE);
        var store = trans.objectStore("todo");

        var data = {
          "text": todoText,
          "timeStamp": new Date().getTime()
        };

        var request = store.put(data);

        request.onsuccess = function(e) {
          todoDB.indexedDB.getAllTodoItems();
        };

        request.onerror = function(e) {
          console.log("Error Adding: ", e);
        };
      };

Fetch Items

After you have data it’s only make sense to show it to the world (and your dear friends)


// WebSQL
function showAll() {
        document.getElementById("ourList").innerHTML = "" ; 
        database.transaction(function(tx) {
          tx.executeSql('SELECT * FROM tasks', [], function (tx, results) {
            var len = results.rows.length, i;
            for (i = 0; i  Todo text: " + results.rows.item(i).text);
              
              var a = document.createElement("a");
              a.textContent = " [Delete]";
              a.setAttribute('data-key', results.rows.item(i).id);
              a.setAttribute('data-val', results.rows.item(i).text);
              a.addEventListener("click", function() {
                deleteTodo(this.getAttribute("data-key"),this.getAttribute("data-val") );
              }, false);
              li.appendChild(t);
              li.appendChild(a);
              document.getElementById("ourList").appendChild(li);
            }
          });        
        });
      }

// IndexedDB
function showAll() {
        document.getElementById("ourList").innerHTML = "" ;   
        var request = window.indexedDB.open("todos");
        request.onsuccess = function(event) {
          // Enumerate the entire object store.
          var db = todoDB.indexedDB.db;
          var trans = db.transaction(["todo"], IDBTransaction.READ_ONLY);
          var request = trans.objectStore("todo").openCursor();
  
          request.onsuccess = function(event) {
            var cursor = request.result || event.result;
            // If cursor is null then we've completed the enumeration.
            if (!cursor) {
              return;
            }
            var element = document.createElement("div");
            element.textContent = "key: " + cursor.key + " => Todo text: " + cursor.value.text;
            document.getElementById("ourList").appendChild(element);
            cursor.continue();
          }
        }                    
      }


Delete Item

In rare cases we wish to delete stuff… It’s easy.


// WebSQL
function deleteTodo(id, text) {
        if (confirm("Are you sure you want to Delete "+ text +"?")) {
          database.transaction(function(tx) {
            tx.executeSql('DELETE FROM tasks WHERE id=?', [id]); 
          });
          showAll();
        } 
      }

// IndexedDB
todoDB.indexedDB.deleteTodo = function(id) {
        var db = todoDB.indexedDB.db;
        var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
        var store = trans.objectStore("todo");

        var request = store.delete(id);

        request.onsuccess = function(e) {
          todoDB.indexedDB.getAllTodoItems();
        };

        request.onerror = function(e) {
          console.log("Error Adding: ", e);
        };
      };


As Oscar Wilde said: “…Consistency is the last refuge of the unimaginative…” – so in our case, let’s save data locally and have more performance in our web apps (with some consistency).

Live Example

All the code is on github - https://github.com/greenido/WebSQL-to-IndexedDB-example

and you can play with a live example.

HTML5 App Caching – The Easy Way

HTML5 features appcaching, a way to make your web sites and apps work offline, and to increase their performance as well.

I’m sure you know, browsers cache HTML, CSS, JavaScript files, images and other resources of the sites you visit, to speed up the subsequent loading of pages. However, you never know when the browser might discard cached files, and so this is not a reliable way for sites to work offline. But what if we could tell the browser what to cache? Well, with HTML5 application caches (also known as ‘appcache’) we can do just that.

An appcache manifest contain several lines in that order:

  • In the first line we declare “CACHE MANIFEST” (required)
  • Second line: “CACHE:” – which specifies the URLs of resources.
  • We can also optionally specify which resources should not be cached, in a section of the manifest file introduced by the string “NETWORK:”. These resources aren’t just not cached, but further, won’t be used when the user is offline, even if the browser has cached them in its own caches.
  • We can also optionally specify fallback resources to be used when the user is not connected, in a section of the file called “FALLBACK:”
  • You can add comments to the file with, simply by beginning a line with “#” – that’s an important feature to make the version readable for you as a developer. It’s also a nice way to let the browser ‘know’ that something changed in our app and it’s needed to fetch a new version of the app from the network.

Here is a simple example:


CACHE MANIFEST
#version 1.0

CACHE:

#images
/images/logo.png
/images/ido-header.png

#pages
/pages/index.html
/pages/main.html

#CSS
/style/main-style.css

#scripts
/js/main-logic.js

FALLBACK:
/ /offline.html

NETWORK:
sign-new-user.html

Creating a HTML5 cache manifest file the easy way:

Other good reads: