IndexedDB open DB request weird behavior - javascript

I have an app (questionnaire) that uses indexedDB.
We have one database and several stores in it.
Stores have data already stored in them.
At some point a dashboard html file is loaded. In this file I am calling couple of functions:
function init(){
adjustUsedScreenHeight();
db_init();
setInstitutionInstRow();
loadRecommendations();
loadResultsFromDB();
fillEvaluations();
document.addEventListener("deviceready", onDeviceReady, function(e) {console.log(e);});
}
The init() function is called on body onLoad.
setInstitutionInstRow() looks like these:
function setInstitutionInstRow(localId){
//localId = 10;
if (localId == undefined){
console.log("Localid underfined: ");
//open db, open objectstore;
var request = indexedDB.open("kcapp_db", "1.0");
request.onsuccess = function() {
var db = request.result;
var tx = db.transaction ("LOCALINSTITUTIONS", "readonly");
var store = tx.objectStore("LOCALINSTITUTIONS");
tx.oncomplete = function(){
db.close();
}
tx.onerror = function(){
console.log("Transaction error on setInstInstRow");
}
var cursor = store.openCursor();
cursor.onsuccess= function () {
var match = cursor.result;
console.log ("Retrieved item: " + match.value.instid);
// alert("Added new data");
if (match){
setInstituionInstRow(match.value.instid);
console.log("Got localid: " + math.value.instid);
}
else
console.log("localinsid: it is empty " );
};
cursor.onerror = function () {
console.log("Error: " + item.result.errorCode);
}
}
request.onerror = function () {
console.log("Error: " + request.result.errorCode );
}
request.oncomplete = function (){
console.log("The transaction is done: setInstitutionRow()");
}
request.onupgradeneeded = function (){
console.log("Upgrade needed ...");
}
request.onblocked = function(){
console.log("DB is Blocked ...");
}
} else {
instid = localId;
var now = new Date();
//console.log("["+now.getTime()+"]setInstituionInstRow - instid set to "+localId);
//open db, open objectstore;
var request = indexedDB.open("kcapp_db", "1.0");
request.onsuccess = function() {
var db = this.result;
var tx = db.transaction ("INSTITUTIONS", "readonly");
var store = tx.objectStore("INSTITUTIONS");
var item = store.get(localId);
console.log(item);
item.onsuccess= function () {
console.log ("Retrieved item: ");
if (item.length > 0)
var lInstitution = item.result.value;
kitaDisplayValue = lInstitution.krippe;
};
item.onerror = function () {
console.log("Error: " + item.result.errorCode);
}
}
request.onerror = function () {
console.log("Error: " + request.result.errorCode );
}
}
Now the problem is,
var request = indexedDB.open("kcapp_db", "1.0");
the above request is never getting into any onsuccess, oncomplete, onerror states. I debugged with Chrome tools, it never getting into any above states.
Accordingly I am not getting any data from transactions.
And there are no errors in Chrome console.
And here is the request value from Chrome dev:
From above image the readyState: done , which means it should fire an event (success, error, blocked etc). But it is not going into any of them.
I am looking into it, and still can not figure out why it is not working.
Have to mention that the other functions from init() is behaving the same way.
Looking forward to get some help.

You may be using an invalid version parameter to the open function. Try indexedDB.open('kcapp_db', 1); instead.

Like Josh said, your version parameter should be an integer, not a string.
Your request object can get 4 events in response to the open request: success, error, upgradeneeded, or blocked. Add event listeners for all of those (e.g. request.onblocked = ...) and see which one is getting fired.

I had that problem but only with the "onupgradeneeded" event. I fixed it changing the name of the "open" function. At the begining I had a very long name; I changed it for a short one and start working. I don't know if this is the real problem but it was solved at that moment.
My code:
if (this.isSupported) {
this.openRequest = indexedDB.open("OrdenesMant", 1);
/**
* Creación de la base de datos con tablas y claves primarias
*/
this.openRequest.onupgradeneeded = function(oEvent) {
...
Hope it works for you as well.

Related

indexedDB openCursor transaction onsuccess returns empty array

req = db.openCursor();
req.customerData=new Array() //[{a:1}]
req.onsuccess = function(e) {
var cursor = e.currentTarget.result;
if (cursor) {
//console.log(cursor.value);
e.currentTarget.customerData.push(cursor.value);
e.currentTarget.customerData.push("hello?");
cursor.continue()
}
else {
console.log(e.currentTarget.customerData) //this always correct
}
}
console.log(req.customerData); //outside the onsuccess everything is gone?
console.log(req);
I can see customerData when I open the object in the chrome console
console.log(req.customerData);
But when I do the above it is empty?
replacing new Array() with [{a:1}]
console.log(req.customerData);
I can see a and also the other objects
but then agian
console.log(req.customerData[0].a);
works and the other objects are gone.
How can I save customerData? I tried just pushing numbers or text but same thing after transaction is done. I can't get the data out only display it on console.log() during the transaction?
I know it must be something past by reference but every variable I trow in dissapears?
Added full example below just type write() and read() in console
<script>
var iDB
ready=function(){
var request = indexedDB.open("my-database",1);
request.onupgradeneeded = function(e) {
var db = e.currentTarget.result
var store = db.createObjectStore("store", {keyPath: "muts", autoIncrement:false})
//store.createIndex("by_submit", "submit", {unique: false})
console.log('db upgrade', 'v'+db.version)
}
request.onerror = function(e) {
//var db = e.currentTarget.result;
//db.close()
console.error('db error ',e)
}
request.onsuccess = function(e) {
var db = e.currentTarget.result
db.onversionchange = function(e) {
db.close()
console.log('db changed', 'v'+db.version, 'CLOSED')
}
console.log('db setup', 'v'+db.version, 'OK')
}
iDB=request
}
drop=function(){
iDB.result.close()
var req = indexedDB.deleteDatabase(this.iDB.result.name);
req.onsuccess = function() {console.log("Deleted database successfully")}
req.onerror = function() {console.log("Couldn't delete database")}
req.onblocked = function() {console.log("Couldn't delete database due to the operation being blocked")}
}
read=function(){
var db=iDB
.result
.transaction(["store"], "readwrite").objectStore("store");
var req = db.openCursor();
req.iData=new Array();
req.onsuccess = function(e) {
var cursor = e.currentTarget.result;
if (cursor) {
e.currentTarget.iData.push(cursor.value);
e.currentTarget.iData.push("hello");
cursor.continue()
}
else {
console.log(e.currentTarget.iData)
}
}
console.log(req.iData)
}
write=function(){
var db=document.querySelector('my\-database')
.iDB
.result
.transaction(["store"], "readwrite").objectStore("store");
var customerData = [
{muts: "Bill", qty: "1"},
{muts: "Donna", qty: "1"}
]
for (var i in customerData){db.put(customerData[i])}
}
ready()
</script>
A few things
I recommend not setting custom properties of an IDBRequest object. Create and access objects that are in an outer scope instead.
There is no need to use event.currentTarget. event.target is sufficient (and so is 'this', and so is the request object itself).
onversionchange is deprecated.
Due to the asynchronous nature of indexedDB, you may be trying to print something out to the console that does not yet exist, or no longer exists. Instead, try printing something out when the transaction completes.
For example:
function populateArray(openDatabaseHandle, onCompleteCallbackFunction) {
var transaction = openDatabaseHandle.transaction('store');
var store = transaction.objectStore('store');
var myArray = [];
var request = store.openCursor();
request.onsuccess = function() {
var cursor = this.result;
if(!cursor) return;
myArray.push(cursor.value);
cursor.continue();
};
transaction.oncomplete = function() {
onCompleteCallbackFunction(myArray);
};
}
// An example of calling the above function
var conn = indexedDB.open(...);
conn.onsuccess = function() {
populateArray(this.result, onPopulated);
};
// An example of a callback function that can be passed to
// the populateArray function above
function onPopulated(data) {
console.debug(data);
data.forEach(function(obj) {
console.debug('Object: %o', obj);
});
}

Ping with javascript and read the errors

I'm basically following the accepted answer to this question (Is it possible to ping a server from Javascript?)
Update
It seems to work as expected when the domain is 15 characters long (actually, http:// + 15, but 16 or more causes it to bomb. More details at the bottom.
The issue I'm seeing is that if you're using something that seems like a valid domain, for example http://thisisdefinitelynotarealdomainname.com, it returns an error but the code mentioned considers errors okay (because most should be). Looking at the error event, I'm not sure I see where I could get the HTTP response code (i.e., if it's a 404, consider it invalid).
Here is a jsFiddle showing the problem -- they all display "responded". If you look in the console, the invalid domain returns a 404 error, and the two valid ones (if in chrome console, not sure about the others) show that they were interpreted as an image but transferred as text/html -- is there any way to read either the 404 error, or the mime type?
var pinger = function () {
var ping = function (ip, callback) {
if (!this.inUse) {
this.status = 'unchecked';
this.inUse = true;
this.callback = callback;
this.ip = ip;
var _that = this;
this.img = new Image();
this.img.onload = function () {
_that.inUse = false;
_that.callback('responded');
};
this.img.onerror = function (e) {
if (_that.inUse) {
_that.inUse = false;
_that.callback('responded', e);
}
console.log(e);
};
this.start = new Date().getTime();
this.img.src = "http://" + ip + "/?now=" + this.start; // add the current time to work around caching
this.time = setTimeout(function () {
if (_that.inUse) {
_that.inUse = false;
_that.callback('timeout');
}
}, 1500);
}
}
return {
ping: ping
};
}();
(function () {
var output = document.getElementById('output');
var servers = [
'localhost',
'google.com',
'okthisreallydoesntmakeanysense',
'okthisreallydoe',
'thisisashortone',
'thisisabitlonger'
];
servers.forEach(function (server) {
new pinger.ping(server, function (status, e) {
output.innerHTML += server + ': ' + status + '<br />';
});
});
})();
Update
What's even more weird is that it seems to be fine up until 15 characters. I've updated the jsFiddle. See below on ones that respond how I'd expect vs ones that don't. What might cause this?
'localhost',
'google.com',
'okthisreallydoesntmakeanysense', // doesn't work
'okthisreallydoe', // works (15 characters)
'thisisashortone', // works (15 characters)
'thisisabitlonger' // doesn't work (16 characters)
This might help.
function Pinger_ping(ip, callback) {
if(!this.inUse) {
this.inUse = true;
this.callback = callback
this.ip = ip;
var _that = this;
this.img = new Image();
this.img.onload = function() {_that.good();};
this.img.onerror = function() {_that.good();};
this.start = new Date().getTime();
this.img.src = "http://" + ip;
this.timer = setTimeout(function() { _that.bad();}, 1500);
}
}
Let me know if it works

Why does onsuccess sometimes get called before onupgradeneeded when connecting to indexedDB?

I am having trouble with IndexedDB. On Firefox 18, when I create a new database, the onsuccess method is called at the same time has onupgradeneeded. On Chrome 24 (this is the behavior I'd like to get), the onsuccess method is only called after the onupgradeneeded method has completed.
According to the MDN information on IndexedDB, I was under the impression that when the onsuccess method was called, it was safe to work with the database but this make it seems like it is not in Firefox.
(function(){
app = {};
// These will hold the data for each store.
app.objectstores = [
{ name: 'UNIVERSITIES',
keyPath: 'UID',
autoIncrement: false,
data_source: 'http://mysites.dev/nddery.ca_www/larelance/data/universite.json' },
];
// Some information pertaining to the DB.
app.indexedDB = {};
app.indexedDB.db = null
app.DB_NAME = 'testdb';
app.DB_VERSION = 1;
/**
* Attempt to open the database.
* If the version has changed, deleted known object stores and re-create them.
* We'll add the data later.
*
*/
app.indexedDB.open = function() {
// Everything is done through requests and transactions.
var request = window.indexedDB.open( app.DB_NAME, app.DB_VERSION );
// We can only create Object stores in a onupgradeneeded transaction.
request.onupgradeneeded = function( e ) {
app.indexedDB.db = e.target.result;
var db = app.indexedDB.db;
// Delete all object stores not to create confusion and re-create them.
app.objectstores.forEach( function( o ) {
if ( db.objectStoreNames.contains( o.name ) )
db.deleteObjectStore( o.name );
var store = db.createObjectStore(
o.name,
{ keyPath: o.keyPath, autoIncrement: o.autoIncrement }
);
app.indexedDB.addDataFromUrl( o.name, o.data_source );
});
}; // end request.onupgradeneeded()
// This method is called before the "onupgradeneeded" has finished..??
request.onsuccess = function( e ) {
app.indexedDB.db = e.target.result;
app.ui.updateStatusBar( 'Database initialized...' );
// ***
// Would like to query the database here but in Firefox the data has not
// always been added at this point... Works in Chrome.
//
}; // end request.onsuccess()
request.onerror = app.indexedDB.onerror;
}; // end app.indexedDB.open()
app.indexedDB.addDataFromUrl = function( store, url ) {
var xhr = new XMLHttpRequest();
xhr.open( 'GET', url, true );
xhr.onload = function( event ) {
if( xhr.status == 200 ) {
console.log('*** XHR successful');
// I would be adding the JSON data to the database object stores here.
}
else{
console.error("addDataFromUrl error:", xhr.responseText, xhr.status);
}
};
xhr.send();
}; // end app.indexedDB.addDataFromUrl()
})();
Thanks!
One of the things you are probably suffering with is the auto-commit functionality in the indexeddb. If an transaction becomes inactive for a short timespan, it will commit the transaction and close it.
In your case you are calling an async method to fill up the data, and that is why the transaction probably becomes inactive. if you add a console.write after app.indexedDB.addDataFromUrl( o.name, o.data_source ); you will see it will get called before your data is retrieved, causing the commit of the transaction. That is why the data isn't present when the success callback is called. It is possible that the timeout for transactions is higher in chrome than in Firefox. It isn't described in the spec so it can vary by vendor.
btw, if you want to add data in your ajax call, you will have to pass the object store as a parameter as well.
do what you have to do inside if( xhr.status == 200 ) {}
call a transaction from there, put the data to the objectStore and what more you need
edit:
I do it like this, works for me, the oncomplete function only gets called once all the data is inserted and the objectStore is ready for use:
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://...", true);
xhr.addEventListener("load", function(){
if(xhr.status === 200){
console.info("Data received with success");
var data = JSON.parse(xhr.responseText);
var transaction = db.transaction([STORe],'readwrite');
var objstore = transaction.objectStore(STORE);
for(i = 0; i < data.length; i++){
objstore.put(data[i]);
};
transaction.oncomplete = function(event){
//do what you need here
console.info("Inserted data: " + data.length);
};
transaction.onerror = function(event){
};
transaction.onabort = function(event){
};
transaction.ontimeout = function(event){
};
transaction.onblocked = function(event){
};
};
}, false);
xhr.send();

Store retrieved data from indexed DB to a variable

I am using the following code to read data from Indexed DB and save it in variable allDownloadContent
ereaderdownload.indexedDB.getAllTodoItems = function() {
/*var todos = document.getElementById("todoItems");
todos.innerHTML = "";
*/
var db = ereaderdownload.indexedDB.db;
var trans = db.transaction(["downloadcontent"], "readwrite");
var store = trans.objectStore("downloadcontent");
var request = store.get(0);
request.onsuccess = function(e) {
console.log(e.target.result);
};
// Get everything in the store;
var cursorRequest = store.openCursor();
cursorRequest.onsuccess = function(e) {
var result = e.target.result;
if(!!result == false)
return;
allDownloadContent.push(result);
result.continue();
};
alert("content "+allDownloadContent[0]);
cursorRequest.onerror = ereaderdownload.indexedDB.onerror;
};
When I call the getAllTodoItems method from another Javascript file I am getting a alert message content undefined
since the cursorRequest.onsuccess method executes async I am getting undefined.
I cannot make use of web workers since it is not supported in chrome.
I tried promise in Jquery. Still I am getting the same alert message.
Please help me in resolving the issue.
As for now all browsers only support the Indexed-db ASync API, and what you need to do is add an event listener to the transaction oncomplete event. This event will fire when cursor is closed. From there you can return to your code:
trans.oncomplete = function (event) {
console.log('transaction completed');
yourFunction();
};

How to use onMessage inside addEventListener?

I save the value of a textarea id="save" inside addEventListener. Then, I send it to the server with xhr and at the same time I open a channel by using Google App Engine Channel API, then I am trying to capture the message sent back with onMessage.
Everything works, except the returned message. I understand that the returned message will be evt.data but it is not logged. Can you help me understand what I am doing wrong? This is a follow up to my previous question. Thanks!
document.getElementById("save").addEventListener
(
"click",
function ()
{
var userEmail = document.getElementById("getEmail").value;
var formData = new FormData();
formData.append("extension_user", userEmail);
var channel;
var socket;
var handler =
{
//I changed this to "onmessage" as draevor's answer but
//I still don't see "evt.data" logged
onMessage: function (evt)
{
//evt.data will be what the server sends in channel.send_message
console.log("evt.data received from authhandler: " + evt.data);
alert("evt.data is: " + evt.data)
}
};
var xhr = new XMLHttpRequest();
xhr.onReadyStateChange = function()
{
//this alert does not trigger
alert("xhr.onReadyStateChange")
if (xhr.readyState == 4 && xhr.status == 200)
{
token = xhr.responseText;
//this alert does not trigger
alert("token: " + token)
channel = new goog.appengine.Channel(token);
socket = channel.open(handler);
}
};
xhr.open("POST", "http://ting-1.appspot.com/authsender", true);
xhr.send(formData);
console.log("formData sent to authsender: " + formData);
}, false
)
UPDATE
Update as suggested by draevor's answer I added other properties of onmessage. I followed this question although I am not sure why he puts the properties in single quotes.
document.getElementById("save").addEventListener
(
"click",
function ()
{
var userEmail = document.getElementById("getEmail").value;
var formData = new FormData();
formData.append("extension_user", userEmail);
var channel;
var socket;
var handler =
{
onopen: onOpen,
onmessage: onMessage,
onerror: onError,
onclose: onClose
};
var xhr = new XMLHttpRequest();
xhr.onReadyStateChange = function()
{
//this alert does not trigger
alert("xhr.onReadyStateChange")
if (xhr.readyState == 4 && xhr.status == 200)
{
token = xhr.responseText;
//this alert does not trigger
alert("token: " + token)
channel = new goog.appengine.Channel(token);
socket = channel.open(handler);
}
};
xhr.open("POST", "http://ting-1.appspot.com/authsender", true);
xhr.send(formData);
console.log("formData sent to authsender: " + formData);
}, false
)
onMessage =
function (evt)
{
//evt.data will be what the server sends in channel.send_message
console.log("evt.data received from authhandler: " + evt.data);
alert("evt.data is: " + evt.data)
}
onOpen =
function ()
{
alert("onOpen")
}
onError =
function ()
{
alert("onError")
}
onClose =
function ()
{
alert("onClose")
}
I think the problem is simply having named the property onMessage instead of onmessage. I would also suggest setting all the other properties (onopen, onerror, onclose), at least for debugging purposes in case the above doesn't solve your problem.
window.onmessage is used for sending messages between frames. The Channel API uses this. So if you create your own window.onmessage -- which you're doing here -- all heck breaks loose.
Just define your handlers inline, or call them something else entirely (handleChannelMessage or what-have-you).

Categories

Resources