In my previous article, I demonstrated how to create an offline app using HTML5 Application Cache API. Link to this article is provided below:-

https://sudiptach.wordpress.com/2017/07/15/asp-net-mvc-offline-app-using-html5-application-cache-api/

In this article, I will extend the solution developed previously and add the functionality to persist data offline using HTML5 offline storage capability provided by Indexed Database popularly referred to as IndexedDb. You can read the IndexedDB W3C specifications at https://www.w3.org/TR/IndexedDB/

Complete source code for the article can be found on GitHub repo at :

https://github.com/sudipta-chaudhari/IndexedDB_AppCache_ASPNET_MVC5

Open /Product/Index view to access the application

Tools Used : Any text editor for creating and writing HTML and Jquery code. I have used Visual Studio 2017

Technologies Used : HTML5, JQuery

I have created an ASP.NET MVC5 solution. You can even create a single HTML5 page.

Plugins Used :

  1. JQuery DataTables 1.10.15 – https://datatables.net/download/packages
  2. JQueryUIhttp://jqueryui.com/download/

Key Learnings :

(1) IndexedDB – Create IndexedDB & perform CRUD – Add, Edit , Delete, View operations

(2) JQueryDataTables – Create grid & perform CRUD – Add, Edit , Delete, View operations

(3) JQueryUI – Create modal popup

Solution Description :

In this application, I will implement CRUD (Create, Read, Update, Delete) operations – Add, Edit, Delete, List etc on IndexedDB.

The application’s home page looks as below.

indexeddb_homepage

Add Product popup looks as below:-

IndexedDB_AddProduct

Edit Product popup looks as below:-

IndexedDB_EditProduct

If you go to the browser’s developer tools, I am using Chrome, Developer tools can be opened using shortcut Ctrl + Shift + I, under Storage section, you will see IndexedDB.

IndexedDB_ChromeDeveloperTools

JQuery DataTable is generated from table tag. div tag with id “dialog-form” is used to display the Add/Edit JQueryUI popup.

The below Javascript code is used to initialize the IndexedDB database.

function initDb() {
    var request = indexedDB.open("ProductDB", 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("product", { keyPath: "id", autoIncrement: true });
    };
}

The below function is used to initialize JQuery DataTable and also add the code for Edit and Delete buttons

//Function to initialize empty JQuery Grid
function initGrid() {

    var table = $('#tblProducts').DataTable({
        data: null,
        columns: [
            { data: "Id", visible: false, searchable: false },
            { data: "Name", className: "dt-center" },
            { data: "Category", className: "dt-center" },
            { data: "Price", className: "dt-center" },
            { data: "Action", className: "dt-center", defaultContent: "<button class='btn-delete'>Delete</button></div>
<button class='btn-edit'>Edit</button>" }
        ]
    });

    $('#tblProducts tbody').on('click', '.btn-delete', function () {

        var selectedProduct = table.row($(this).parents('tr')).data();

        var prd = new Array();

        prd.push({
            "Id": selectedProduct["Id"],
            "Name": selectedProduct["Name"],
            "Category": selectedProduct["Category"],
            "Price": selectedProduct["Price"]
        });

        deleteItem(selectedProduct["Id"]);
    });

    $('#tblProducts tbody').on('click', '.btn-edit', function () {
        var selectedProduct = table.row($(this).parents('tr')).data();

        $("#txtId").val(selectedProduct["Id"]);
        $("#txtName").val(selectedProduct["Name"]);
        $("#txtCategory").val(selectedProduct["Category"]);
        $("#txtPrice").val(selectedProduct["Price"]);

        $('#dialog-form').dialog('option', 'title', 'Edit Product');
        $("#dialog-form").dialog("open");
    });
}

Note : Edit and Delete button clicks have been identified by adding classes btn-edit and btn-delete.

The initDb() and initGrid() functions are called from window.onload() function as below.

window.onload = function () {
    initDb();
    initGrid();
}

The below function displays all the data from the IndexedDB on the HTML5 page in JQuery DataTable as below.

function showAllItems() {
    var transaction = db.transaction(["product"], "readwrite");
    var objectStore = transaction.objectStore("product");
    var request = objectStore.openCursor();

    request.onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
            product.push({
                "Id": cursor.key,
                "Name": cursor.value.name,
                "Category": cursor.value.category,
                "Price": cursor.value.price
            });
            cursor.continue();
        }
        else {
            //After reading all products from IndexedDB, add all product data to JQuery Grid
            $('#tblProducts').DataTable().rows.add(product).draw();

        };
    }
}

Function to add new item to the IndexedDB with values from the JQueryUI popup is as below:

function addNewItem() {
    var pName = $('#txtName').val();
    var pCategory = $('#txtCategory').val();
    var pPrice = $('#txtPrice').val();

    var transaction = db.transaction(["product"], "readwrite");
    var objectStore = transaction.objectStore("product");
    var request = objectStore.add({ name: pName, category: pCategory, price: pPrice });

    request.onsuccess = function (event) {
        //Refresh JQuery grid with newly added data
        var prd = new Array();

        prd.push({
            "Id": event.target.result,
            "Name": pName,
            "Category": pCategory,
            "Price": pPrice
        });

        product.push({
            "Id": event.target.result,
            "Name": pName,
            "Category": pCategory,
            "Price": pPrice
        });

        $('#tblProducts').DataTable().rows.add(prd).draw();
    };
}

Function to edit existing item to the IndexedDB with values from the JQueryUI popup is as below:

function editExistingItem() {
    var transaction = db.transaction(["product"], "readwrite");
    var objectStore = transaction.objectStore("product");

    var pId = parseInt($("#txtId").val());

    var result = product.find(x => x.Id == pId);
    var i = product.indexOf(result);

    objectStore.get(pId).onsuccess = function (event) {
        var rec = event.target.result;

        rec.id = pId;
        rec.name = $("#txtName").val();
        rec.category = $("#txtCategory").val();
        rec.price = $("#txtPrice").val();

        if (i != -1) {
            product[i].Id = rec.id;
            product[i].Name = rec.name;
            product[i].Category = rec.category;
            product[i].Price = rec.price;
        }

        objectStore.put(rec).onsuccess = function (event) {
            $('#tblProducts').DataTable().clear();
            $('#tblProducts').DataTable().rows.add(product).draw();
            console.log('Updated successfully!');
        }
    }
}

Save button code to Add/Edit record in IndexedDB is as below:-

$("#dialog-form").dialog({
    autoOpen: false,
    height: 300,
    width: 350,
    modal: true,
    buttons: {
        "Save": function () {
            if ($("#txtId").val() == 0) {
                addNewItem();
            }
            else {
                editExistingItem();
            }

            $(this).dialog("close");
        },
        Cancel: function () {
            $(this).dialog("close");
        }
    },
    close: function () {
    }
});

Add route ‘/Product/Index’ to the application manifest file to cache it for it to be available offline. The complete cache manifest file looks as below.

CACHE MANIFEST
# version 1

CACHE:

/Content/bootstrap.css
/Content/Site.css
/Content/Style.css

/Scripts/jquery-1.10.2.js
/Scripts/bootstrap.js
/Scripts/modernizr-2.6.2.js
/Scripts/respond.js

/home
/home/index
/product/index

NETWORK:
 *

I hope you followed the article. If you have any comments, questions or suggestions, leave a message and I will try to respond at my earliest.