I am writing a Chrome extension that utilizes IndexedDB
to store some information client side in an IDBObjectStore
within an IDBDatabase
.
The nature of the data is such that I need my users to be able to modify the object store at their whim. (add new objects modify existing ones etc.) I am accomplishing this through a settings page and all is fine and dandy so far.
The caveat comes when I want to release a new version of the (default) object store. If I didn't care about overwriting my users' data, then I could just handle the onupgradeneeded
event the same way I handle it when it is fired in reaction to a fresh install. Which would be something like:
var request = window.indexedDB.open(DB_NAME, CURRENT_DB_VERSION);
request.onupgradeneeded = upgrade;
function upgrade(event){
var db = event.target.result;
var objectStore = db.createObjectStore("domains", {keyPath: "id", autoIncrement: true});
objectStore.createIndex("domain", "domain", {multiEntry: true });
for(var i=0; i<tags.length; i++){
objectStore.add(tags[i]);
console.log("added " + tags[i]["domain"] + " to the IDBObjectStore 'domains' in the IDBDatabase 'Tags' (Format)");
}
}
...but I do care.
So, what I am currently doing is placing a conditional inside of the upgrade(event)
function that checks for the truthyness of event.oldVersion
(its 0 on a fresh install). If the value is greater than 0, then I open a new tab that contains an options page where the user can pick and choose which objects he wants to update.
Now the tricky part: Once this page closes I need to send a message to the background page that contains the necessary upgrade information. Normally, I would just receive the message in my listener and perform the update, utilizing the provided information. However, IDBDatabase.createObjectStore()
throws an InvalidStateError
if:
The method [is] not called from a versionchange transaction callback.
When I look at the spec for IDBDatabase.transaction(storenames, mode)
, in the mode
parameter description, it says:
versionchange
mode can't be specified here.
So, it seems to me like I need to trigger an onupgradeneeded
event, but I also need to pass the event handler an additional parameter, besides the event itself, which contains information that it can use to decide how to perform the upgrade.
I don't know how to go about doing this though.
Can anyone offer me some insight?
You are getting
InvalidStateError
because you may not be callingIDBDatabase.createObjectStore()
fromonupgradeneeded
event handler. In case of IDB, all object store creation and manipulation has to happen from inside ofonupgradeneeded
event handler or afteronupgradeneeded
is triggered.If I understood your requirement correctly then you do not want to override the user's existing object store in certain conditions, and for that you want to pass some information in
onupgradeneeded
event handler to tell whether to create afresh object store or do some modification on top of existing object store.My recommendation would be - have 2 global array variables
DB_SCHEMA_DROP_QUERIES
andDB_SCHEMA_CREATE_QUERIES
which you can prepare with drop and create data before opening the database usingwindow.indexedDB.open(DB_NAME, CURRENT_DB_VERSION);
Then have code as below (I am just giving a heads up), so when you want to 1. create afresh database then first drop all your existing data stores from database and create new (which means populate both
DB_SCHEMA_DROP_QUERIES
andDB_SCHEMA_CREATE_QUERIES
). 2. just add one more object store then only prepareDB_SCHEMA_CREATE_QUERIES
3. modify an existing object store then prepare bothDB_SCHEMA_DROP_QUERIES
andDB_SCHEMA_CREATE_QUERIES
but only for that particular object storeBasically what we are trying to achieve to making the things dynamic instead of hard-coding data store creation like this
db.createObjectStore("domains", {keyPath: "id", autoIncrement: true});
. This will also help you to get rid of maintaining version records.