2017年2月3日 星期五

網頁離線存取IndexedDB

因為客戶的需求必須要有離線做資料登打,後續再做資料上傳,雖然最後又不要使用,但我還是研究了一下把做法記下來
  1. 在HTML頁面上的html tag 裡加上manifest="~/cache.appcache",這個檔案是參考亞斯狼的教學,利用MVC去產生需要被快取的檔案,包含html、js、css,結構如下
    CACHE MANIFEST
    #V1.0

    需要快取檔案的相對路徑

    NETWORK:
    需要連線的檔案相對路徑

    FALLBACK:
    允許將用戶轉入一個指定的頁面
  2.  原則上只要簡單的頁面上面這樣設定就可以了,那如果要應付較複雜的表單就要在利用前端的資料存取,最簡單的就是使用LoaclStorage及SessionStorage,相容性也高,離線存取用LocalStorage比較適合,畢竟可以一直存在電腦裡,SessionStorage只要關掉瀏覽器資料就會清掉了,但因本次的專案有比較複雜,要有模擬AJAX取資料的動作,因為Storage只能存字串,再某些情況下變得很不好用,所以這邊我用IndexedDB來當我的前台資料庫。
  3.  IndexedDB原則上是非同步的方式在處理資料,一開始在寫他的方法時還遇到蠻多問題,對JS的非同步不太熟^_^;;;,程式碼如下,基本上簡單的資料庫存取都可以。
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    var IDBTransaction = window.IDBTransaction || window.webkitTransaction || window.msIDBTransaction || { READ_WRITE: "readwrite" };
    var AccountDB;
    //
    
    function DBClass(tableName, columns) {
        this.TableName = tableName;
        this.Columns = columns;
        this.DBObject = undefined;
    }
    
    DBClass.prototype.CreateTable = function () {
        var that = this;
        var request = indexedDB.open(that.TableName);
        //
        request.onsuccess = function () {
            that.DBObject = request.result;
        };
        request.onerror = function (event) {
            console.log("IndexedDB error: " + event.target.errorCode);
        };
        request.onblocked = function (event) {
            console.log("IndexedDB on blocked");
            event.target.result.close();
        };
        request.onupgradeneeded = function (event) {
            var hasKey;
            var objectStore;
            //var tableName = that.TableName;
            //
            if (that.Columns) {
                hasKey = that.Columns.some(function (element, index, array) {
                    if (element.Key) {
                        objectStore = event.currentTarget.result.createObjectStore(that.TableName, {
                            keyPath: element.ColumnName, autoIncrement: true
                        });
                        return true;
                    } else {
                        return false;
                    }
                });
                if (!hasKey) {
                    objectStore = event.currentTarget.result.createObjectStore(that.TableName, {
                        keyPath: "SerialNumber", autoIncrement: true
                    });
                }
                that.Columns.forEach(function (element, index, array) {
                    if (!element.Key) {
                        objectStore.createIndex(element.ColumnName, element.ColumnName, { unique: element.Unique });
                    }
                });
            }
        };
    };
    
    DBClass.prototype.AddData = function (columnsValueArray) {
        var transaction = this.DBObject.transaction(this.TableName, "readwrite");
        var objectStore = transaction.objectStore(this.TableName);
        var request;
        var columnsObject = {};
        columnsValueArray.forEach(function (element, index, array) {
            columnsObject[element.ColumnName] = element.Value;
        });
        request = objectStore.add(columnsObject);
        //
        request.onsuccess = function (event) {
            console.log("data save success!");
        };
        request.onerror = function (event) {
            console.log("data save error!");
        };
    };
    
    DBClass.prototype.UpdateData = function (columnsValueArray) {
        var transaction = this.DBObject.transaction(this.TableName, "readwrite");
        var objectStore = transaction.objectStore(this.TableName);
        var request;
        var columnsObject = {};
        columnsValueArray.forEach(function (element, index, array) {
            columnsObject[element.ColumnName] = element.Value;
        });
        request = objectStore.put(columnsObject);
        //
        request.onsuccess = function (event) {
            console.log("data update success!");
        };
        request.onerror = function (event) {
            console.log("data update error!");
        };
    };
    
    DBClass.prototype.DeleteData = function (keyValue) {
        var transaction = this.DBObject.transaction(this.TableName, "readwrite");
        var objectStore = transaction.objectStore(this.TableName);
        var request;
        request = objectStore.delete(keyValue);
        request.onsuccess = function (event) {
            console.log("data delete success!");
        };
        request.onerror = function (event) {
            console.log("data delete error!");
        };
    };
    
    DBClass.prototype.GetData = function (keyValue, getFunc) {
        var transaction = this.DBObject.transaction(this.TableName);
        var objectStore = transaction.objectStore(this.TableName);
        var request;
        request = objectStore.get(keyValue);
        objectStore.op
        //
        request.onsuccess = function (event) {
            if (getFunc) {
                getFunc(event.target.result);
            }
        };
        request.onerror = function (event) {
            console.log("data search error!");
        };
    };
    
    DBClass.prototype.GetAll = function (getFunc) {
        var transaction = this.DBObject.transaction(this.TableName);
        var objectStore = transaction.objectStore(this.TableName);
        var cursor = objectStore.openCursor();
        //
        cursor.onsuccess = function (event) {
            if (getFunc) {
                getFunc(event.target.result);
            }
        };
    };
    
    DBClass.prototype.DropTable = function () {
        if (this.DBObject) {
            this.DBObject.close();
        }
        var request = indexedDB.deleteDatabase(this.TableName);
        request.onsuccess = function () {
            console.log("Delete database successfully");
        };
        request.onerror = function () {
            console.log("Delete database error");
        };
        request.onblocked = function (event) {
            console.log("Database is on blocked, it's can't delete");
        };
    };
    
    var ColumnInfoClass = function (columnName, key, unique) {
        this.ColumnName = columnName;
        this.Key = key === undefined ? false : key;
        this.Unique = unique === undefined ? false : unique;
    };
    
    var ColumnValueClass = function (columnName, value) {
        this.ColumnName = columnName;
        this.Value = value;
    };
    
    function TestDB() {
        var columns = [];
        columns.push(new ColumnInfoClass("ID", true, true));
        columns.push(new ColumnInfoClass("Name"));
        columns.push(new ColumnInfoClass("Phone"));
        AccountDB = new DBClass("Account", columns);
        AccountDB.CreateTable();
    }
    
    function AddDB() {
        var values = [];
        values.push(new ColumnValueClass("ID", 1));
        values.push(new ColumnValueClass("Name", "測試新增"));
        values.push(new ColumnValueClass("Phone", "123456"));
        //
        AccountDB.AddData(values);
    }
    
    function UpdateDB() {
        var values = [];
        values.push(new ColumnValueClass("ID", 1));
        values.push(new ColumnValueClass("Name", "測試修改"));
        values.push(new ColumnValueClass("Phone", "654321"));
        //
        AccountDB.UpdateData(values);
    }
    
    function DeleteDB() {
        AccountDB.DeleteData(1);
    }
    
    function GetDB() {
        AccountDB.GetData(1, function (request) {
            if (request) {
                console.log(request.Name + "_" + request.Phone);
            } else {
                console.log("No Data!");
            }
        });
    }
    
    function GetAll() {
        AccountDB.GetAll(function (request) {
            if (request) {
                console.log(request.value.Name);
                request.continue();
            }
        });
    }