Cannot create second objectStore IndexedDB - javascript

I basically have this code to generate a new database with one objectStore:
DB.open_or_create({table: 'photos', key: 'url'});
And the main code:
var DB = {
open_or_create : function(p){
var openRequest = indexedDB.open("new_app",1);
openRequest.onupgradeneeded = function(e) {
var thisDB = e.target.result;
if(!thisDB.objectStoreNames.contains(p.table)) {
thisDB.createObjectStore(p.table,{ keyPath: p.key });
}
}
openRequest.onsuccess = function(e) {DBTABLES = e.target.result}
openRequest.onerror = function(e) {console.log(e)}
}
Then i tried to add a second objectStore with the command:
DB.open_or_create2({table: 'users',key: 'id'});
And basically the same code only with another database version number:
open_or_create2 : function(p){
var openRequest = indexedDB.open("new_app",2);
openRequest.onupgradeneeded = function(e) {
var thisDB = e.target.result;
if(!thisDB.objectStoreNames.contains(p.table)) {
if(p.key){
thisDB.createObjectStore(p.table,{ keyPath: p.key });
}else{
thisDB.createObjectStore(p.table)
}
}
}
openRequest.onsuccess = function(e) {DBTABLES = e.target.result}
openRequest.onerror = function(e) {console.log(e)}
}
But as i can see in the console only the first table gets created! Also when i try to add data to the patients table i get this error:
Uncaught NotFoundError: Failed to execute 'transaction' on 'IDBDatabase': One of the specified object stores was not found.
What do i wrong? Thanks!

In the course of development you probably already increased new_app to version 2. Bump it up to version 3 in your above code and it will work. If not, add a openRequest.onblocked handler and add console.log lines to all your event handlers to see which one is getting fired.

Related

IndexedDB undefined request

I'm using IndexedDB for the first time and i'm still trying to figure it all out.
I (think I) added items in the IndexedDB on a page and on the next page i need to retrieve them.
Here's a portion of the code:
var db;
const dbName = "biometricData";
const osName = "userdata";
var request = indexedDB.open(dbName, 1); /*Creazione del database*/
request.onsuccess = function(event) {
db = this.result; /*Salvo l'istanza del database al momento della crazione*/
alert ("Database creato");
updateBatteryData();
};
request.onerror = function(event) {
var error = request.error;
alert(error);
};
request.onupgradeneeded = function(event) {
/*Chiave di ricerca e creazione dell'objectstore*/
db = this.result;
alert("Upgrade Needed");
if(!db.objectStoreNames.contains(osName)) {
alert("Devo creare l'Object Store "+osName);
var objectStore = db.createObjectStore(osName, { keyPath: "id", autoIncrement:true });
objectStore.createIndex("student","student", {unique: false});
objectStore.createIndex("type","type", {unique: false});
}
};
This is to create the database at first
function updateBatteryData() {
batteryData = {id:id,
student:matr,
type:6,
index:batteryDataIndex,
time:data["time"],
batteryLevel:data["batteryLevel"],
isCharging:data["isCharging"],
dischargingTime:data["dischargingTime"]};
var transaction = db.transaction([osName], "readwrite");
var objectStore = transaction.objectStore(osName);
var request = objectStore.add(batteryData);
request.onsuccess = function(event)
{
alert("salvato il primo valore di batteria");
}
}
Then i call this function to save the object in the DB (of course all of these values are not undefined).
Also, this is always id = 1.
Then, on the next page, i try to retrieve the data. I open the database at the same way i open it before and then i use this:
var transaction = db.transaction([osName]);
var objectStore = transaction.objectStore(osName);
var request = objectStore.get(1);
request.onerror = function(event) {
alert("error");
};
request.onsuccess = function(event) {
alert("done");
alert(event.target.result.type);
};
The thing is this: if i create the database for the first time, get the values, save them, then retrieve them on the next page it doesn't do anything, it says that the value is undefined. If I do the same thing for the second time with the database already created it actually finds the data.
What am I missing?

Global variable only accessible to first function?

I have an OK understanding of JS but am ultimately still learning. I'm trying to recreate a PHP/mySQL project over to IndexedDB and can't work out why I'm seeing an error here.
The IndexedDB connection works as expected. The first function (createItem) functions fine, however the second function (getItems) is returning an error claiming that the "db" variable is undefined. It's a global variable so should be accessible by the function's scope, and the createItem function has no problem using it. Can anyone help me see what I've missed here.
// GLOBAL DB VAR
var db;
// WAIT FOR DOM
document.addEventListener("DOMContentLoaded", function(){
// IF INDEXED DB CAPABLE
if("indexedDB" in window) {
// OPEN DB
var openRequest = indexedDB.open("newTabTest",1);
// CREATE NEW / UPDATE
openRequest.onupgradeneeded = function(e) {
// Notify user here: creating database (first time use)
var thisDB = e.target.result;
// Create "items" table if it doesn't already exist
if(!thisDB.objectStoreNames.contains("items")) {
var store = thisDB.createObjectStore("items", {keyPath: "id", autoIncrement: true});
store.createIndex("name","name", {unique:true});
store.createIndex("folder","folder", {unique:false});
store.createIndex("dial","dial", {unique:false});
}
}
openRequest.onsuccess = function(e) {
// Success- set db to target result.
db = e.target.result;
}
openRequest.onerror = function(e) {
// DB ERROR :-(
}
}
},false);
// CREATE ITEM FUNCTION
function createItem(n,u,f,c) {
var transaction = db.transaction(["items"],"readwrite");
var store = transaction.objectStore("items");
var item = {
name: n,
url: u,
folder: f,
colour: c,
dial: 0,
order: 100
}
var request = store.add(item);
request.onerror = function(e) {
console.log("An error occured.");
}
request.onsuccess = function(e) {
console.log("Successfully added.")
}
};
// GET ITEM(S) FUNCTION
// Specify index and key value OR omit for all
function getItems(callback, ind, key) {
var transaction = db.transaction(["items"],"readonly");
var store = transaction.objectStore("items");
var response = [];
// If args are omitted - grab all items
if(!ind | !key) {
var cursor = store.openCursor();
cursor.onsuccess = function(e) {
var res = e.target.result;
if(res) {
var r = {
"name": res.value['name'],
"url": res.value['url'],
"folder": res.value['folder'],
"colour": res.value['colour'],
"dial": res.value['dial'],
"order": res.value['order']
};
response.push(r);
res.continue();
}
}
cursor.oncomplete = function() {
callback(response);
}
} else {
// If both args are specified query specified index
store = store.index(ind);
var range = IDBKeyRange.bound(key, key);
store.openCursor(range).onsuccess = function(e) {
var res = e.target.result;
if(res) {
var r = {
"name": res.value['name'],
"url": res.value['url'],
"folder": res.value['folder'],
"colour": res.value['colour'],
"dial": res.value['dial'],
"order": res.value['order']
};
response.push(r);
res.continue();
}
}
cursor.oncomplete = function() {
callback(response);
}
}
};
As you've figured out in comments, you do have to stick the db-dependent actions inside a function called from a success handler.
This callback-based programming quickly becomes a pain, and a common solution for that is to use promises.
However, making IndexedDB work with promises is still work-in-progress (see https://github.com/inexorabletash/indexeddb-promises if you're interested).
If your goal is to get something done (and not to learn the bare IndexedDB APIs), perhaps you'd be better off finding a wrapper library for IndexedDB (can't recommend one though, since I've not tried working seriously with IDB yet).
I would not recommend using a variable like 'db' as you have in your example. If you are new to reading and writing asynchronous Javascript, you are just going to cause yourself a lot of pain. There are better ways to do it. It takes several pages to explain and is explained in many other questions on StackOverflow, so instead, very briefly, consider rewriting your code to do something like the following:
function createItem(db, ...) {
var tx = db.transaction(...);
// ...
}
function openIndexedDBThenCreateItem(...) {
var openRequest = indexedDB.open(...);
openRequest.onsuccess = function(event) {
var db = event.target.result;
createItem(db, ...);
};
}
function getItems(db, callback, ...) {
var tx = db.transaction(...);
var items = [];
tx.oncomplete = function(event) {
callback(items);
};
// ...
var request = store.openCursor(...);
request.onsuccess = function(event) {
var request = event.target;
var cursor = event.target.result;
if(cursor) {
var item = cursor.value;
items.push(item);
cursor.continue();
}
};
}
function openIndexedDBThenGetItems(db, callback, ...) {
var openRequest = indexedDB.open(...);
openRequest.onsuccess = function(event) {
var db = event.target.result;
getItems(db, callback, ...);
};
}
Also, you don't need to wait for DOMContentLoaded to start using indexedDB. It is immediately available.
If you get the above code, then you can consider a further improvement of adding a simple helper function:
function openIndexedDB(callback) {
var openRequest = indexedDB.open(...);
openRequest.onerror = callback;
openRequest.onsuccess = callback;
}
And then rewrite the examples like this:
function openIndexedDBThenCreateItem(...) {
openIndexedDB(function onOpen(event) {
if(event.type !== 'success') {
console.error(event);
} else {
var db = event.target.result;
createItem(db, ...);
}
});
}
function openIndexedDBThenGetItems(...) {
openIndexedDB(function onOpen(event) {
if(event.type !== 'success') {
console.error(event);
} else {
var db = event.target.result;
getItems(db, ...);
}
});
}

How to get all values from indexeddb

I am working on storing some data in the indexedDb.
I have created a method which saves the data into the indexedDb. I have stored exactly 49 records. I am trying to retrieve all of them. I have written the below code for getting the values. No other code except this line exist in my js file.
function crap() {
var indexedDb = window.indexedDB || window.webkitIndexedDB || window.msIndexedDB;
var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
var openedDb = indexedDb && indexedDb.open;
var isIndexDbTransactionPossible = window.IDBTransaction || window.webkitIDBTransaction;
if (isIndexDbTransactionPossible) {
isIndexDbTransactionPossible.READ_WRITE = isIndexDbTransactionPossible.READ_WRITE || 'readwrite';
isIndexDbTransactionPossible.READ_ONLY = isIndexDbTransactionPossible.READ_ONLY || 'readonly';
}
var request = indexedDb.open('Offline', DB_VERSION);
request.onupgradeneeded = function(e) {
var db = e.target.result;
if (db.objectStoreNames.contains('tab')) {
db.deleteObjectStore('tab');
}
var store = db.createObjectStore('tab', {keyPath: 'id', autoIncrement: true});
};
request.onsuccess = function(e) {
console.log("DB opened");
var db = e.target.result;
var store= db.transaction('tab', IDBTransaction.READ_ONLY).objectStore('tab');
var cursor = store.openCursor();
cursor.onsuccess = function(event) {
var c = event.target.result;
if (c) {
console.log("New value")
c.continue();
}
};
};
}
I am seeing "New Value" printed 124 times. I am not sure why the cursor.continue() is not returning null after 49th attempt. Any help is much appreciated.
I am positive that this method is not called more than one time. "DB opened" is logged only one.
Just use the getAll function:
var allRecords = store.getAll();
allRecords.onsuccess = function() {
console.log(allRecords.result);
};
Read more in the documentation: Working with IndexedDB
Instead of checking readyState, just check for whether the cursor is defined in your cursor request callback. Here is an example. I modified the names of your variables slightly for clarity.
cursorRequest.onsuccess = function(event) {
var cursor = event.target.result;
if(cursor) {
var value = cursor.value;
console.log('New value:', value);
cursor.continue();
} else {
// Undefined cursor. This means either no objects found,
// or no next object found
// Do not call cursor.continue(); in this else branch because
// there are no more objects over which to iterate.
// Coincidentally, this also means we are done iterating.
console.log('Finished iterating');
}
}

IndexedDB Reference Error: db is not defined

I'm trying to create an application for Firefox OS which basically needs to store some data using IndexedDB.
The store() function is called when the user clicks on a submit button which results in the creation of the name and description variables an user submitted form.
However, I keep getting a Reference Error saying my db object is not defined. Any ideas why this is happening?
Here's my current code:-
function store () {
// create the transaction with 1st parameter is the list of stores and the second specifies
// a flag for the readwrite option
var transaction = db.transaction([ 'Apps' ], 'readwrite');
//Create the Object to be saved i.e. our App details
var value = {};
value.name = name;
value.desc = description;
// add the details to the store
var store = transaction.objectStore('Apps');
var request = store.add(value);
request.onsuccess = function (e) {
alert("Your App data has been saved");
};
request.onerror = function (e) {
alert("Error in saving the App data. Reason : " + e.value);
}
}
$(document).ready(function(){
// variable which will hold the database connection
var db;
if (window.indexedDB) {
console.log("IndexedDB is supported");
}
else {
alert("Indexed DB is not supported!");
}
// open the database
// 1st parameter : Database name. We are using the name 'Appsdb'
// 2nd parameter is the version of the database.
var request = indexedDB.open('Appsdb', 1);
request.onsuccess = function (e) {
// e.target.result has the connection to the database
db = e.target.result;
console.log(db);
console.log("DB Opened!");
}
request.onerror = function (e) {
console.log(e);
};
// this will fire when the version of the database changes
// We can only create Object stores in a versionchange transaction.
request.onupgradeneeded = function (e) {
// e.target.result holds the connection to database
db = e.target.result;
if (db.objectStoreNames.contains("Apps")) {
db.deleteObjectStore("Apps");
}
// create a store named 'Apps'
// 1st parameter is the store name
// 2nd parameter is the key field that we can specify here. Here we have opted for autoIncrement but it could be your
// own provided value also.
var objectStore = db.createObjectStore('Apps', { keyPath: 'id', autoIncrement: true });
console.log("Object Store has been created");
};
});
The problem is with the scope of the db variable. Currently you have the following line var db; declared inside of the $(document).ready function. Move its declaration to a more global scope i.e. outside of this function and the variable will be visible in the store() function too.
Hope this helps.
var value = {};
value.name = name;
value.desc = description;
assign value to the name and description.
name=formname.name.value
description=formname.description.value

IndexedDB handle data migration onupgradeneeded

i'm developing a offline-webapplication with IndexedDB. So I thought a lot about data migration in case of a version change.
For example, I had 3 ObjectStores in DB Version 3. Now i noticed, that i should have a specific index at all 3 ObjectStores. But its not possible to add an index afterwards to an existing ObjectStore, without losing data.
What could be the solution to handle data migration in an "onupgradeneeded"-event?
No Need to kill the StoreObject just update it like this :
request.onupgradeneeded = function(evt) {
var dataBase = evt.target.result;
var txn = evt.target.transaction;
//////////
var storeCreateIndex = function (objectStore, name, options) {
if (!objectStore.indexNames.contains(name)) {
objectStore.createIndex(name, name, options);
}
}
//////////
var catalogItem, mangaItem, chapterItem, artworkItem;
if (evt.newVersion != evt.oldVersion) {
// Get exiting objectStore
catalogItem = txn.objectStore('CatalogItem');
mangaItem = txn.objectStore('MangaItem');
chapterItem = txn.objectStore('ChapterItem');
artworkItem = txn.objectStore('ArtworkList');
} else {
// Fist creation of database objectStore
catalogItem = dataBase.db.createObjectStore("CatalogItem", { keyPath: "key" });
mangaItem = dataBase.db.createObjectStore("MangaItem", { keyPath: "key" });
chapterItem = dataBase.db.createObjectStore("ChapterItem", { keyPath: "key" });
artworkItem = dataBase.db.createObjectStore("ArtworkList", { keyPath: "key" });
}
//////////
storeCreateIndex(catalogItem, "popularity", { unique: false });
storeCreateIndex(catalogItem, "author", { unique: false });
storeCreateIndex(catalogItem, "status", { unique: false });
storeCreateIndex(catalogItem, "isFavorite", { unique: false });
storeCreateIndex(chapterItem, "isBookmarked", { unique: false });
storeCreateIndex(chapterItem, "isDownloaded", { unique: false });
}
As noted in the comments above:
Attempting to initiate a new transaction from "onupgradeneeded" will result the error:
InvalidStateError: DOM IDBDatabase Exception 11
Instead use the transaction referenced by the request object. Example:
function opendb(oncomplete){
var version = 1;
var migrateobjects = [];
var request = indexedDB.open('mydb', version);
request.onupgradeneeded = function(e) {
db = e.target.result;
transaction = e.target.transaction;
if(db.objectStoreNames.contains('myobjects')){
migraterequest = transaction.objectStore('myobjects').openCursor();
migraterequest.onsuccess = function(e){
var cursor = e.target.result;
if (cursor){
migrateobjects.push(cursor.value);
cursor.continue();
}
};
db.deleteObjectStore('myobjects');
}
var store = db.createObjectStore('myobjects', {keyPath: 'id'});
};
request.onsuccess = function(e) {
db = e.target.result;
transaction = db.transaction('myobjects', 'readwrite')
store = transaction.objectStore('myobjects');
for(var i=0; i < migrateobjects.length; ++i)
store.put(migrateobjects[i]);
transaction.oncomplete = oncomplete;
};
};
Changing index should not clear existing records unless new index violate database constraint of existing records. But I do observe object store blow away in Chrome but not in Firefox.

Categories