IndexedDB undefined request - javascript

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?

Related

Get specifics ids in IndexedDB [duplicate]

I want to execute this query
select * from properties where propertyCode IN ("field1", "field2", "field3")
How can I achieve this in IndexedDB
I tried this thing
getData : function (indexName, params, objectStoreName) {
var defer = $q.defer(),
db, transaction, index, cursorRequest, request, objectStore, resultSet, dataList = [];
request = indexedDB.open('test');
request.onsuccess = function (event) {
db = request.result;
transaction = db.transaction(objectStoreName);
objectStore = transaction.objectStore(objectStoreName);
index = objectStore.index(indexName);
cursorRequest = index.openCursor(IDBKeyRange.only(params));
cursorRequest.onsuccess = function () {
resultSet = cursorRequest.result;
if(resultSet){
dataList.push(resultSet.value);
resultSet.continue();
}
else{
console.log(dataList);
defer.resolve(dataList);
}
};
cursorRequest.onerror = function (event) {
console.log('Error while opening cursor');
}
}
request.onerror = function (event) {
console.log('Not able to get access to DB in executeQuery');
}
return defer.promise;
But didn't worked. I tried google but couldn't find exact answer.
If you consider that IN is essentially equivalent to field1 == propertyCode OR field2 == propertyCode, then you could say that IN is just another way of using OR.
IndexedDB cannot do OR (unions) from a single request.
Generally, your only recourse is to do separate requests, then merge them in memory. Generally, this will not have great performance. If you are dealing with a lot of objects, you might want to consider giving up altogether on this approach and thinking of how to avoid such an approach.
Another approach is to iterate over all objects in memory, and then filter those that don't meet your conditions. Again, terrible performance.
Here is a gimmicky hack that might give you decent performance, but it requires some extra work and a tiny bit of storage overhead:
Store an extra field in your objects. For example, plan to use a property named hasPropertyCodeX.
Whenever any of the 3 properties are true (has the right code), set the field (as in, just make it a property of the object, its value is irrelevant).
When none of the 3 properties are true, delete the property from the object.
Whenever the object is modified, update the derived property (set or unset it as appropriate).
Create an index on this derived property in indexedDB.
Open a cursor over the index. Only objects with a property present will appear in the cursor results.
Example for 3rd approach
var request = indexedDB.open(...);
request.onupgradeneeded = upgrade;
function upgrade(event) {
var db = event.target.result;
var store = db.createObjectStore('store', ...);
// Create another index for the special property
var index = store.createIndex('hasPropCodeX', 'hasPropCodeX');
}
function putThing(db, thing) {
// Before storing the thing, secretly update the hasPropCodeX value
// which is derived from the thing's other properties
if(thing.field1 === 'propCode' || thing.field2 === 'propCode' ||
thing.field3 === 'propCode') {
thing.hasPropCodeX = 1;
} else {
delete thing.hasPropCodeX;
}
var tx = db.transaction('store', 'readwrite');
var store = tx.objectStore('store');
store.put(thing);
}
function getThingsWherePropCodeXInAnyof3Fields(db, callback) {
var things = [];
var tx = db.transaction('store');
var store = tx.objectStore('store');
var index = store.index('hasPropCodeX');
var request = index.openCursor();
request.onsuccess = function(event) {
var cursor = event.target.result;
if(cursor) {
var thing = cursor.value;
things.push(thing);
cursor.continue();
} else {
callback(things);
}
};
request.onerror = function(event) {
console.error(event.target.error);
callback(things);
};
}
// Now that you have an api, here is some example calling code
// Not bothering to promisify it
function getData() {
var request = indexedDB.open(...);
request.onsuccess = function(event) {
var db = event.target.result;
getThingsWherePropCodeXInAnyof3Fields(db, function(things) {
console.log('Got %s things', things.length);
for(let thing of things) {
console.log('Thing', thing);
}
});
};
}

Use of indexedDB returns 'undefined'

I'm trying to use indexedDB.
Some parts of my code works.
In the following example, the first function adds server in my DB, however in Chrome debug console there is an undefined message not related to any line. The server is already added though.
The second function puts records in an array, there is also an undefined message not related to any line.
If I do a console.log(servers); just before return servers; I can see the array content, however if I call the function somewhere else in my code, the returned object is undefined.
var dbName = 'myDBname',
dbServersStoreName = 'servers',
dbVersion = 1,
openDBforCreation = indexedDB.open(dbName, dbVersion);
openDBforCreation.onupgradeneeded = function(e) {
var db = e.target.result;
var objStore = db.createObjectStore(dbServersStoreName, { keyPath: "alias"
});
var index = objStore.createIndex("serversAlias", ["alias"]);
};
function addServerInDB(serverAlias,serverAddress,user,pwd){
var myDB = indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var db = e.target.result,
request = db.transaction([dbServersStoreName],
"readwrite").objectStore("servers")
.put({alias:''+serverAlias+'',
address:''+serverAddress+'', login:''+user+'',
passwd:''+pwd+''});
request.onsuccess = function(){
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Server added'});
}
}
};
function listServersInDB(){
var myDB= indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var servers = new Array(),
db = e.target.result,
request = db.transaction(["servers"], "readwrite")
.objectStore("servers")
.openCursor();
request.onsuccess = function(e){
var cursor = e.target.result;
if(cursor){
servers.push(cursor.value);
cursor.continue();
}
return servers;
}
}
};
I do not understand where this undefined comes from and if that is why the listServersInDB() function doesn't work.
You need to learn more about how to write asynchronous Javascript. There are too many errors in your code to even begin reasoning about the problem.
Briefly, don't do this:
function open() {
var openDatabaseRequest = ...;
}
openDatabaseRequest.foo = ...;
Instead, do this:
function open() {
var openDatabaseRequest = ...;
openDatabaseRequest.foo = ...;
}
Next, you don't need to try and open the same database multiple times. Why are you calling indexedDB.open twice? You can open a database to both install it and to start using it immediately. All using the same connection.
Next, I'd advise you don't name the database open request as 'myDB'. This is misleading. This is an IDBRequest object, and more specifically, an IDBOpenRequest object. A request isn't a database.
Next, you cannot return the servers array from the request.onsuccess at the end. For one this returns to nowhere and might be source of undefined. Two this returns every single time the cursor is advanced, so it makes no sense at all to return return servers multiple times. Three is that this returns too early, because it cannot return until all servers enumerated. To properly return you need to wait until all servers listed. This means using an asynchronous code pattern. For example, here is how you would do it with a callback:
function listServers(db, callbackFunction) {
var servers = [];
var tx = db.transaction(...);
var store = tx.objectStore(...);
var request = store.openCursor();
request.onsuccess = function() {
var cursor = request.result;
if(cursor) {
servers.push(cursor.value);
cursor.continue();
}
};
tx.oncomplete = function() {
callbackFunction(servers);
};
return 'Requested servers to be loaded ... eventually callback will happen';
}
function connectAndList() {
var request = indexedDB.open(...);
request.onsuccess = function() {
var db = request.result;
listServers(db, onServersListed);
};
}
function onServersListed(servers) {
console.log('Loaded servers array from db:', servers);
}
When you call a function that does not return a value, it returns undefined. All functions in JavaScript return undefined unless you explicitly return something else.
When you call a function from the devtools console, and that function returns undefined, then the console prints out '-> undefined'. This is an ordinary aspect of using the console.
If you want to get a function that returns the list of servers as an array, well, you cannot. The only way to do that in a pretend sort of way, is to use an 'async' function, together with promises.
async function getServers() {
var db = await new Promise(resolve => {
var request = indexedDB.open(...);
request.onsuccess = () => resolve(request.result);
});
var servers = await new Promise(resolve => {
var tx = db.transaction(...);
var request = tx.objectStore(...).getAll();
request.onsuccess = () => resolve(request.result);
});
return servers;
}
One more edit, if you want to call this from the console, use await getServers();. If you do not use the top-level await in console, then you will get the typical return value of async function which is a Promise object. To turn a promise into its return value you must await it.
Clear and helpfull explanations, Thank you.
I open database multiple times beacause the first time is for checking if DB needs an upgrade and doing something if needed. I'll add 'db.close()' in each functions.
Then, I tried your exemple and the result is the same:
console.log('Loaded servers array from db:', servers); works
but return servers; Don't work.
And in console there is already an undefined without related line :
Screenshot

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, ...);
}
});
}

Update indexedDB

Hi I am trying to update records indexdb. I want to implement a generic method that allows me to upgrade to any field.
To do this I have a function that returns me the record I need.
function getObjectStoreClienteDB(clave,valor){
//Ahora recogeremos los datos de nuestro almacén "Productos"
var transaction = db.transaction(["customers"], "readwrite");
var objectStore = transaction.objectStore("customers");
var index = objectStore.index(clave);
var singleKeyRange = IDBKeyRange.only(valor);
// To use one of the key ranges, pass it in as the first argument of openCursor()/openKeyCursor()
return index.openCursor(singleKeyRange);
}
And another that updates the returned record
function updateClienteDB(clave,valor,newvalor){
console.log("updateClienteDB ... clave: "+clave+" valor: "+valor+" newvalor: "+newvalor);
var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
request = getObjectStoreClienteDB("name",valor);
request.onsuccess = function(event) {
// Get the old value that we want to update
var data = request.result;
// update the value(s) in the object that you want to change
if(clave=="name")
data.name = newvalor;
else if(clave=="email")
data.email = newvalor;
else if(clave=="matricula")
data.matricula = newvalor;
else if(clave=="telefono")
data.telefono = newvalor;
// Put this updated object back into the database.
var requestUpdate = objectStore.put(data);
requestUpdate.onerror = function(event) {
console.log("addCliente ..."+name+" "+email +" "+ event.target.errorCode);
};
requestUpdate.onsuccess = function(event) {
console.log("All done!");
};
};
}
In the line:
var requestUpdate = objectStore.put(data);
error:
Uncaught TransactionInactiveError: Failed to execute 'put' on 'IDBObjectStore': The transaction has finished.
Try using the same readwrite transaction. It looks like you are calling db.transaction(...) in two places. You are then trying to update an object store linked to a different transaction in the context of a different transaction. The result is that one of the transactions completes (its lifetime ends), probably because it times out, because it does not detect any registered requests. The objectStore variable lives within the lifetime of the transaction that completes early, so you get a transaction finished error.
This is trivial to fix. You simply need to register a new request (the put request) on the same transaction before its lifetime ends. To do this, you can either (a) inline the statement request = getObjectStoreClienteDB("name",valor); so that it is attached to the same transaction instance, or (b) store the transaction instance in a separate variable and reference that in both calls.
For example, the following code is a modification of yours that uses a single transaction:
// this function was changed, it now takes an active transaction as its first argument.
function getObjectStoreClienteDB(transaction, clave,valor){
//Ahora recogeremos los datos de nuestro almacén "Productos"
// this was changed. instead of creating a new transaction we just reference the
// transaction instance passed to this function
var objectStore = transaction.objectStore("customers");
var index = objectStore.index(clave);
var singleKeyRange = IDBKeyRange.only(valor);
// To use one of the key ranges, pass it in as the first argument of openCursor()/openKeyCursor()
return index.openCursor(singleKeyRange);
}
function updateClienteDB(clave,valor,newvalor){
console.log("updateClienteDB ... clave: "+clave+" valor: "+valor+" newvalor: "+newvalor);
// this line was added. we create the transaction here, once
var transaction = db.transaction(["customers"], "readwrite");
// this line was changed, we simply reference the transaction instance here instead of
// creating a second transaction
var objectStore = transaction.objectStore("customers");
// this line was changed, we pass in the one active transaction instance to the function
// now instead of having the function create its own transaction
request = getObjectStoreClienteDB(transaction, "name",valor);
request.onsuccess = function(event) {
// Get the old value that we want to update
var data = request.result;
// update the value(s) in the object that you want to change
if(clave=="name")
data.name = newvalor;
else if(clave=="email")
data.email = newvalor;
else if(clave=="matricula")
data.matricula = newvalor;
else if(clave=="telefono")
data.telefono = newvalor;
// Put this updated object back into the database.
// this line now works. objectStore is attached to our 1 transaction that is still 'alive'
// because it gets registered 'in time' (before transaction finishes due to timeout due
// to no additional requests registered in allowed time window).
var requestUpdate = objectStore.put(data);
requestUpdate.onerror = function(event) {
console.log("addCliente ..."+name+" "+email +" "+ event.target.errorCode);
};
requestUpdate.onsuccess = function(event) {
console.log("All done!");
};
};
}
And, just to be clear, here is a second example. It re-creates a reference to the object store using the transaction of the caller, instead of cross referencing an object store attached to a different transaction:
function updateClienteDB(clave,valor,newvalor){
console.log("updateClienteDB ... clave: "+clave+" valor: "+valor+" newvalor: "+newvalor);
var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
request = getObjectStoreClienteDB("name",valor);
request.onsuccess = function(event) {
// Get the old value that we want to update
var data = request.result;
// update the value(s) in the object that you want to change
if(clave=="name")
data.name = newvalor;
else if(clave=="email")
data.email = newvalor;
else if(clave=="matricula")
data.matricula = newvalor;
else if(clave=="telefono")
data.telefono = newvalor;
// Put this updated object back into the database.
// the following line is changed so that it works.
//var requestUpdate = objectStore.put(data);
var theOtherTransactionThatIsStillAlive = event.transaction;
var objectStoreFromValidTransaction = theOtherTransactionThatIsStillAlive.objectStore('customers');
var requestUpdate = objectStoreFromValidTransaction.put(data);
requestUpdate.onerror = function(event) {
console.log("addCliente ..."+name+" "+email +" "+ event.target.errorCode);
};
requestUpdate.onsuccess = function(event) {
console.log("All done!");
};
};
}

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

Categories