Jason
Jason

Reputation: 4145

Indexed DB: Database Not Initialized

So, for my weekend project, I chose learning IndexedDB. I'm trying to create something like a DAO (sorry, it's the Java developer in me). Here's what I have:

function ReceiptDB() {

    this.db = null;
    this.dbname = "Receipts";
    this.dbver = 2;

    this.getDB = function() {
        return db;
    };

    this.setDB = function(dbresult) {
        db = dbresult;
    };


    var topThis = this;
    this.initialize = function() {

        alert("Running init");

        var promise = $.Deferred();

        var request = indexedDB.open(this.dbname, this.dbver);

        request.onsuccess = function(e) {
            topThis.db = e.target.result;
            topThis.setDB(e.target.result);
            promise.resolve();
        };

        request.onupgradeneeded = function(e) {
            console.log("Upgrading database");
            var db = event.target.result;
            if (!db.objectStoreNames.contains("GroupIDs")) {
                var groupIdObjectStore = db.createObjectStore("GroupIDs");
            }

            if (!db.objectStoreNames.contains("Locations")) {
                var storesObjectStore = db.createObjectStore("Locations");
            }

        };

        request.onfailure = function(e) {
            alert("failed to open db");

            //topThis.initPromise.reject();
            promise.reject();
        };

        request.onerror = function(e) {
            alert("init error: " + e.target.errorCode);
            promise.reject();
        };

        return promise;
    };

    this.getAllGroupIds = function() {

        var promise = $.Deferred();

        var results = [];

        var db = topThis.db;
        if (!db) {
            alert("DB not initialized!");
        }

        var tx = topThis.db.transaction("GroupIDs", "readonly");
        var store = tx.objectStore("GroupIDs");

        store.openCursor().onsuccess = function(e) {
            var cursor = event.target.result;
            if (cursor) {
                results.push(cursor.value);
                cursor.continue();
            }

            promise.resolve(results);
        };

        return promise;
    };

    this.init();


};

I trigger this with an HTML button:

$(document).ready(function() {

    $("#btn1").click(function(e) {

        var receiptdb = new ReceiptDB();
        var initPromise = receiptdb.initialize();

        $.when(initPromise).done(function() {
            console.log("receiptdb promise done");
            var getAllPromise = receiptdb.getAllGroupIds();
        });

    });
}

I'm using the promise to ensure that the object initializes the database before I attempt to run a query against it. Unfortunately, it's not working: when I call the getAllGroupIds() method, the db is null. To me, this should have been taken care of via the init method.

Can anyone tell me where I went wrong?

Edit: I renamed the init function to initialize() and am calling it manually, because it makes little sense for a constructor to call a value. Jason

Edit 2: Before I saw Josh answer, I changed gears. I still use promises, though.

function openDatabase(DBNAME, DBVER) {

var promise = $.Deferred();

var request = window.indexedDB.open(DBNAME, DBVER);

request.onsuccess = function(e) {
    console.log("Successfully opened DB");
    promise.resolve(e.target.result);
};

request.onupgradeneeded = function(e) {
    console.log("configuring database");
    // create datastores and indexes
};

request.onfailure = function(e) {
    promise.reject();
};

request.onerror = function(e) {
    console.log("Error opening database: " + e.target.errorCode);
    promise.reject();
};

}

At this point, I could create a Repository, as long as the promise resolved:

function Repository(database) {
    this.db = database;

     this.get = function(id) {
         // get an item by id, return a promise (again) since the
         // result is inside another onsuccess callback
     }

     // rest of crud functions
}
var dbPromise = openDatabase("mydb", 1);
$.when(dbPromise).done(function(db) {
    var repo = new Repository(db);

    var resultPromise = repo.get(id);
    $.when(resultPromise).done(function(result)) {
        // do something with the result
    };
});

I don't like the nested $.when much, but it seems to be working for me

Upvotes: 0

Views: 1251

Answers (1)

Josh
Josh

Reputation: 18710

First guess, it is when you assign e.target.result to topThis.db. You're assigning a value that is only guaranteed to be valid within its scope, or dependent scopes, to an independent outer scope.

I would suggest spending some time learning about writing async js before continuing. Also, I would personally recommend avoiding promises because given the nature of the issue you're experiencing all you are really doing is increasing the complexity of the problem and even obscuring it.

Briefly, instead of referencing the global db variable in response to the onclick, open a new connection. Next, pass that around as the first parameter to your other functions. Also, as related to writing async code, instead of trying to write functions that return values, instead, write functions that pass results to callback functions.

To get you started, ensure you understand the following:

var x;
function setOuterX() {
  x = 'Hello World';
}
setTimeout(setOuterX, 0);
console.log(x);

Upvotes: 1

Related Questions