Windows Store Apps: Using IndexedDB for Storing Data using HTML5 and JavaScript

Posted by: Mahesh Sabnis , on 3/4/2013, in Category WinRT
Views: 12547
Abstract: When developing Windows Store Apps using HTML5 and JavaScript, IndexedDB can be used for storing Application data locally when the device is unable to connect to an external server. This article demonstrates how to use IndexedDB in a Windows 8 Store App

While developing applications for devices (e.g. Phone, Tablet), it is often necessary for the developer to think of various mechanisms of storing data locally. Devs often use SQLite, Local File System, Isolated Storage, IndexedDB etc. Except for SQLite and IndexedDB, other techniques require a minimum amount of custom plumbing (reading/writing/accessing in memory/searching etc.).

If you are using JavaScript and HTML5, then as a developer you have the option of using IndexedDB. IndexedDB allows developers to create on device database and perform CRUD operations .

 

When developing Windows Store Apps using HTML5 and JavaScript, IndexedDB can be used for storing Application data locally. This is ideal for disconnected mode apps. For example, we have an order booking application that a Medical Representative uses to book orders at a pharmacist. The mobile device uploads data to the company server when connected and when not connected, saves the data locally. This requires the data to be stored such that merge operation with server can be completed easily. IndexedDB is an option for similar scenarios. In the following steps, we will see the use of IndexedDB in Windows Store Apps using JavaScript and HTML.

IndexedDB is a ‘developing’ web standard for a database. This holds records with simple values and hierarchical objects. In Windows Store apps using JavaScript, IndexedDB has an Asynchronous API. The record in IndexedDB is stored as key-values. One of the best feature of IndexedDB is that the API is provided in such a way that the record from the store can be accessed using its key or index set on it. This makes development easy.

Building a Windows 8 Store App using WinJS and IndexedDB

An IndexedDB can be created using:

var orderDB = window.indexedDB.open("OrdersDB", 1);

More information about IndexedDB can be read from here and from here.

Step 1: Open VS 2012 and create a new Windows Store App using JavaScript. Name it as ‘Store_Working_with_IndexDB’.

Step 2: Open Default.html and add the below HTML in it:

<body>
<p>Content goes here</p>
<table>
  <thead>
   <tr>    <td style="font-size:xx-large">Manage Medicine Orders</td>    </tr>
  </thead>
  <tr>    <td>Order Id</td>    <td>
    <input type="text" id="txtordno" required="required" />
   </td>
  </tr>
  <tr>    <td>Pharmacist Name</td>     <td>
    <input type="text" autocomplete="on" id="txtpharmacist"  required="required"/>
   </td>
  </tr>
  <tr>    <td>Order Date   </td>
   <td>   <div id="datePicker" data-win-control="WinJS.UI.DatePicker"      data-win-options="{current: '01/01/2013',maxYear:2040,minYear:2012}"></div>   </td>
  </tr>
  <tr>    <td>Medicine  Ordered   </td>
   <td>     <input type="text" id="txtordermedicine"  required="required"/>    </td>
  </tr>
  <tr>    <td>Medicine  Quantity    </td>
   <td>     <input type="text" id="txtqty" required="required" />    </td>
  </tr>
  <tr>    <td>Unit Price    </td>
   <td>     <input type="text" id="txtunitprice"  required="required"/>    </td>
  </tr>
<tfoot>

<—Some code has been truncated for better readability. Please check the download for additional code –>
</tfoot>
</table>
<!--To display Table Dynamically-->
<div id="dvData"></div>
</body>

Locate the <div> with id as ‘datepicker’, this div is set to the DatePicker control provided by WinJS library using data-win-control attribute. The attribute data-win-options sets the current value to be displayed in control along with maximum year and minimum year to be displayed.

Step 3: Add a script tag in the default.html and add the following script in it:

var selectedDate = "";
var orderDB;
var dbCreated = false;
var orderToEdit = {};
var transaction;
var tbl;
WinJS.UI.processAll().done(function () {

document.getElementById('btnnew').addEventListener("click", neworder, false);
document.getElementById('btnsave').addEventListener("click", saveOrder,
  false);
document.getElementById('btnedit').addEventListener('click', editOrder,
  false);
document.getElementById('btndelete').addEventListener('click', deleteOrder,
  false);
            
document.getElementById('btnloadrecord').addEventListener('click',   
  loadOrderById, false);

datePickerControl = document.getElementById('datePicker').winControl;
datePickerControl.current = new Date(); //The Current Date
selectedDate = datePickerControl.current; //The Default Date
datePickerControl.addEventListener("change", dateChanged, false);
});

//The method to Get the Selected Date from the Date Picker
function dateChanged(evt) {
selectedDate = datePickerControl.current;
if (selectedDate < new Date())
{
  var Msg = new Windows.UI.Popups.MessageDialog("Date Cannot be less than Todays Date");
  Msg.showAsync();
  return;
}
}
//Ends Here
//Method to Create Database and Table
function createdatabase()
{
//code to create DB
}
//Ends Here
//The Method to Create New Order
function neworder()
{
//Code to clear all Textboxes
}
//Ends Here
//The method to Save Record
function saveOrder()
{
}
//Ends Here
//The method to Edit Record
function editOrder()
{
//Code to Edit Record
}
//Ends Here
//The Method to Delete Record
function deleteOrder()
{
//Code to delete record
}
//Ends Here
//The Metod to Read all Records
function loadOrder()
{
//Code to load all Records here
}
//Ends Here
//The method to Load the Record by Id
function loadOrderById(id)
{
//Method to Load Record for a specific Id
}
//Ends Here
//The Initial method to be executed
window.addEventListener("DOMContentLoaded", createdatabase, false);

The above script defines click event for each button and the method for setting the selected date. The method ‘dateChanged’ is used to extract the selected data by the user. This method shows the message box if the selected date is less than current date.

Step 4: Since the IndexedDB programming in JavaScript uses Asynchronous API, as a developer we need to be careful while implementing each step. Write the following code in ‘createdatabase’ method

function createdatabase() {
orderDB = window.indexedDB.open("OrdersDB", 1); //Name of the database
//Create Object Store
orderDB.onupgradeneeded = function (e) {
  var ordDb = e.target.result;
  transaction = e.target.result
  //Create Table. Here the Name of the Table
  //is 'NewOrderStore' and the key is set to name as 'id'
  tbl = ordDb.createObjectStore("NewOrderStore", { keyPath: "id"});
  //The column metadata
  var columnOptions = { unique: false, multientry: false };
  //Define the Columns
  tbl.createIndex("PharmacistName1", "PharmacistName", columnOptions);
  tbl.createIndex("OrderDate1", "OrderDate", columnOptions);
  tbl.createIndex("OrderMedicine1", "OrderMedicine", columnOptions);
  tbl.createIndex("Quantity1", "Quantity", columnOptions);
  tbl.createIndex("UnitPrice1", "UnitPrice", columnOptions);
  tbl.createIndex("TotalPrice1", "TotalPrice", columnOptions);
  var md = new Windows.UI.Popups.MessageDialog("DB Created");
  md.showAsync();
};
 
//If the DB creation is successful
orderDB.onsuccess = function (e) {
  if (dbCreated) {
   var dd = e.target.result;
   transaction = e.target.result;
   var dbName = transaction.name;
   loadOrder(); //Load Orders if Table is already Available
  }
};
dbCreated = true;
}

  • The ‘open’ method opens the database after creating it.
  • The event ‘upgradeneeded’ is fired when the database is created with the version number set. Here in this case, the database name is ‘OrdersDB’ and version number is ‘1’.
  • Once the database is created, now the ‘createObjectStore’ is used to create an object store in which records will be stored. Here the name of an object store is ‘NewOrderStore’ with the key set as ‘id’.
  • Once the object store is created, other columns can be added in it using ‘createIndex’ method. This accepts parameters as index name, kaypath name and other metadata regarding column as ‘unique’ etc.

Step 5: Add the following code in ‘neworder’ method which clears all textboxes.

function neworder()
{
document.getElementById('txtordno').value = "";
document.getElementById('txtpharmacist').value = "";
document.getElementById('txtordermedicine').value = "";
document.getElementById('txtqty').value = "";
document.getElementById('txtunitprice').value = "";
document.getElementById('txtprice').value = "";
}

Step 6: Add the following code in ‘loadOrder’ method:

function loadOrder() {
//   debugger;
var orders = [];
var resHTML = "";
//S1: open the database
var db = window.indexedDB.open("OrdersDB");
if (db)
{
  db.onsuccess = function (e) {
  //S2: Get transaction object for the Object Store for performing operation
  var transaction =e.target.result.transaction("NewOrderStore", "readonly");
  //S3: Begin the transaction on the object store
  var objectStore = transaction.objectStore("NewOrderStore");
  //S4: On the completion of the transaction generate HTML table dynamically
  transaction.oncomplete = function () {
   resHTML = "<table border='1'><tr><th>OrderId</th><th>StockitestName</th><th>OrderDate</th><th>OrderMedicine</th><th>OrderQuantity</th><th>UnitPrice</th><th>TotalPrice</th></tr>";
   for (var i = 0; i < orders.length; i++) {
    resHTML += "<tr><td>" + orders[i].id + "</td><td>" +
    orders[i].PharmacistName + "</td><td>" +
    orders[i].OrderDate + "</td><td>" + orders[i].OrderMedicine +
    "</td><td>" + orders[i].Quantity +
    "</td><td>" + orders[i].UnitPrice + "</td><td>" +
    orders[i].TotalPrice + "</td></tr>";
   }
   resHTML+="</table>"
                       
   var resDiv = document.getElementById('dvData');
   resDiv.innerHTML = resHTML;
  };


  //S5: Set the Cursor for Reading Orders from object store. This points to the first record in the source
  var orderCursor = objectStore.openCursor();
  //S6: Add each record in the Array of name 'orders'
  orderCursor.onsuccess = function (e) {
   var reader = e.target.result;
   if (reader) {
     orders.push(reader.value);
     reader.continue();
    }
   };
  };
}
}

As mentioned in the comments, the above method is responsible for reading all orders stored in an object store. Like any database, IndexedDB has the same logical steps:

  • Open Database.
  • Get the object store for performing transaction.
  • Begin transaction.

Set cursor which points to the first record in the store.

Step 7: Add the following code in the ‘saveOrder’ method. This code is responsible for saving record in the object store:

function saveOrder() {
var db = window.indexedDB.open("OrdersDB");
if (db) {
  db.onsuccess = function (e) {
  //S1: Get the Transaction for the ObjectStore, here in this case it is for readwrite
  var orderStore = e.target.result.transaction("NewOrderStore", "readwrite");
  //S2: Get the object store object
  var tbl = orderStore.objectStore("NewOrderStore");
  //S3: Read values entered in each textbox and also the selected date
  var id = document.getElementById('txtordno').value;
  var sockiestName = document.getElementById('txtpharmacist').value;
  var orderDate = selectedDate;
  var orderMedicine = document.getElementById('txtordermedicine').value;
  var quantity = document.getElementById('txtqty').value;
  var unitProce = document.getElementById('txtunitprice').value;
  var totalPrice = parseInt(document.getElementById('txtunitprice').value) * parseInt(document.getElementById('txtqty').value);
  //S4: Add the values against each keypath on object store
  var saveOperation = tbl.add({
    "id": id,
    "PharmacistName": sockiestName ,
    "OrderDate":orderDate ,
    "OrderMedicine": orderMedicine,
    "Quantity": quantity,
    "UnitPrice": unitProce,
    "TotalPrice": totalPrice 
   });
   saveOperation.onsuccess = function (e) {
    var res = document.getElementById("txtres");
    res.value = "Saved" + e.target.result;
   };
   saveOperation.onerror = function (e) {
    var res = document.getElementById("txtres");
    res.value = "Error Occured" + e.value;
   };
   document.getElementById('txtprice').value = totalPrice;
  };
loadOrder(); //Load Orders if Table is already Available
}
}

The ‘add’ method saves the new record in the object store. Data entered into the textboxes and by the date-picker control is passed to the ‘add’ method.

Note how values are passed to ‘add’ method, it takes the keypath name and value as an expression similar to JSON. The complete execution of ‘add’ method can either be success or error event where the necessary action can be taken.

Step 8: Add the following code in ‘loadOrderById’ method. This is used to display the record details based on the ‘id’ value entered in the ‘OrderId’ textbox. This method provides records which can be used to Edit or Delete.

function loadOrderById(id)
{
var ordno = document.getElementById('txtordno');
id = ordno.value;
//S1: Open the database
var db = window.indexedDB.open("OrdersDB");
if (db) {
  db.onsuccess = function (e) {
  //S2: Begin the transaction on the object store
  var transaction = e.target.result.transaction("NewOrderStore", "readonly");
  var objectStore = transaction.objectStore("NewOrderStore");
  //S3: Set the cursor based upon the id (key) of the record
  var orderCursor = objectStore.openCursor(id);
  //S4: Show the record in the textboxes and datepicker control
  orderCursor.onsuccess = function (e) {
  var reader = e.target.result;
  if (reader) {
     document.getElementById('txtpharmacist').value = reader.value.PharmacistName;
     datePickerControl.current = reader.value.OrderDate;
     document.getElementById('txtordermedicine').value = reader.value.OrderMedicine;
     document.getElementById('txtqty').value = reader.value.Quantity;
     document.getElementById('txtunitprice').value = reader.value.UnitPrice;
     document.getElementById('txtprice').value = reader.value.TotalPrice;
    }
   };
  };
}
}

The above code is same as the loadOrder method, the only difference is that instead on reading all records, it locates a record based upon the value of ‘id’ passed to the ‘openCursor’ method.

Step 9: To update the record, add the following code in ‘editOrder’ method:

function editOrder() {
var ordno = document.getElementById('txtordno');
var id = ordno.value;
//S1: Open Database
var db = window.indexedDB.open("OrdersDB");
if (db) {
  db.onsuccess = function (e) {
  //S2: Set the transaction on the ObjectStore for read-write operation
   var transaction = e.target.result.transaction("NewOrderStore", "readwrite");
   var tbl = transaction.objectStore("NewOrderStore");
   //S3: Use 'get' method to get the record details
   var requestToUpdate = tbl.get(id).onsuccess = function (e) {
   //S4: Assign the record to the 'Order object' of name 'orderToEdit'
    orderToEdit = e.target.result;
    //S5: Assign the New values
    orderToEdit.PharmacistName =
     document.getElementById('txtpharmacist').value;
    orderToEdit.OrderDate = selectedDate;
    orderToEdit.OrderMedicine =
     document.getElementById('txtordermedicine').value;
    orderToEdit.Quantity = document.getElementById('txtqty').value;
    orderToEdit.UnitPrice = document.getElementById('txtunitprice').value;
    orderToEdit.TotalPrice =
     parseInt(document.getElementById('txtunitprice').value) *
     parseInt(document.getElementById('txtqty').value);
    //S6: Update the Record using Put method
    var req = tbl.put(orderToEdit);
    req.onsuccess = function (e) {
    var res = document.getElementById("txtres");
    res.value = "Update Completed Successfully" + e.target.result;
    document.getElementById('txtprice').value = orderToEdit.TotalPrice;
   };
   req.onerror = function (e) {
    var res = document.getElementById("txtres");
    res.value = "Error Occured" + e.target.result;
   };
  };
};
loadOrder();
}
}

The above code is performs the following

  • - Opens the database
  • - Sets the transaction for performing “readwrite” operations.
  • - Searches the record based upon id. Here it uses the ‘get’ method to which the ‘id’ is passed.
  • - The search record is stored into the ‘orderToEdit’ object.
  • - The new values are assigned to each key in the object.
  • - The modified object is passed to the ‘put’ method.

Step 10: Add the following code into the ‘deleteOrder’ method. The method performs the following operations:

- Open the database

- Set the transaction for performing “readwrite” operations.

- Call the ‘delete’ method and pass the ‘id’ to it.

function deleteOrder() {
var ordno = document.getElementById('txtordno');
id = ordno.value;
//S1: Open the database
var db = window.indexedDB.open("OrdersDB");
if (db) {
  db.onsuccess = function (e) {
//S2: Set the transaction to perform 'readwrite' operation
var transaction = e.target.result.transaction("NewOrderStore", "readwrite");
var objectStore = transaction.objectStore("NewOrderStore");
//S3: Delete the record using 'delete' method.
var delRequest = objectStore.delete(id);
  };
}
loadOrder();
neworder();
}

Step 11: Run the application, you will get a message box with a message as ‘DB Created’

winrt-indexdb-created

Add data in the textboxes, and click on the ‘Save’ button. The result will be as below:

indexdb-save-record

Click on the ‘New’ button and all textboxes will be cleared. Add a couple of records. To perform ‘Edit’ OR ‘Delete’ operations enter the ‘Order Id’ in the Textbox and click on ‘Load Record’ button; the record information will be displayed which can be changed and then click on ‘Edit’ button, the updated information will be displayed in the Table below.

When the ‘Delete’ button is clicked, the record will be deleted and removed for the Table below.

Note: To delete the Database and Object Store use the following code

var dbRequest = window.indexedDB.deleteDatabase("OrdersDB");
dbRequest.onsuccess = function ()
{
var md2 = new Windows.UI.Popups.MessageDialog(" Db Deleted");
md2.showAsync();
};

You can add button and execute the above code on its click event.

Conclusion

IndexedDB can be effectively used to store the data generated from the application. This is necessary when the device is unable to connect to an external server using some service (WEB Service/WCF Service/WEB API). IndexedDB provides the guarantee that the data entered by the user is available with device unless it is deleted.

The entire source code of this article can be downloaded at https://github.com/dotnetcurry/winrt-indexeddb-sample

Give me a +1 if you think it was a good article. Thanks!
Recommended Articles


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by sachinsnimbalkar on Thursday, March 7, 2013 1:46 AM
Thanks for writing such great article with simple demonstration.
Comment posted by khusi singh on Saturday, March 9, 2013 12:05 AM
Thanks for sharing it with us. It has helped me a lot.
Visit this link:- http://kushagrabajaj1.blogspot.com/
Comment posted by vmvini on Friday, April 12, 2013 8:16 PM
Thanks for this amazing and very simple guide, much easier and praticle than microsoft samples.
Comment posted by juan fco on Saturday, May 4, 2013 11:58 PM
excelent, great job,
Comment posted by Rohit on Saturday, January 18, 2014 2:38 AM
Great Article Sir.
Sir 1 question, i am new to mobile apps, how the data will be transferred to server database if there is internet connection. Like u said it will store the data locally in IndexDB and when connected to company server will transfer the data to that server.

Please suggest. I am very eager to know this.

Thanks
Rohit
Comment posted by Rohit on Saturday, January 18, 2014 4:10 AM
Great Article Sir.
Sir 1 question, i am new to mobile apps, how the data will be transferred to server database if there is internet connection. Like u said it will store the data locally in IndexDB and when connected to company server will transfer the data to that server.

Please suggest. I am very eager to know this.

Thanks
Rohit

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel