6

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!

Josh
  • 17,834
  • 7
  • 50
  • 68
nddery
  • 88
  • 1
  • 6

2 Answers2

5

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.

Kristof Degrave
  • 4,142
  • 22
  • 32
  • The timeout for transactions being higher in Chrome than in Firefox would make sense. I'll try to make synchronous AJAX (which is kind of counter productive) request and see if the transaction is kept open. Would pre-fetching my data and not adding it to the DB in an AJAX call solve this ? Any other options ? – nddery Feb 17 '13 at 23:41
  • Maybe take an other point of view. Think about this. Keep the situation like it is now. In the onupgradeneeded event you trigger the ajax call and you let the version_change transaction commit. When you receive the callback of the AJAX call, you create a new connection to the db so you can insert the retrieved data. Of course this is a very simple representation on how to do it, but give it a shot. – Kristof Degrave Feb 18 '13 at 09:39
1

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();
Ruben Teixeira
  • 514
  • 2
  • 8
  • 17
  • 1
    Yes, this is what I am doing, however, the `window.indexedDB.open` `oncomplete()` method gets called before I am done adding all the data in the DB - which leads me to issues. I am wondering if there is a full proof way to be alerted when it is safe to use the DB. – nddery Feb 17 '13 at 23:35
  • This is the way I did it at first but it always ended up with the `oncomplete` method to be called early and some `DOM IDBDatabase exception 11` errors. I tried moving the XMLHttpRequest directly in `onupgradeneeded` without much success. Since then, I am pre-fetching my data before attempting to open my database, and adding it directly in the `onupgradeneeded` mehtod, I am not getting any of the above mentioned error/problem. The only downside I see to this approach is that currently I am always fetching my data, even when there is no update needed. – nddery Feb 18 '13 at 16:28
  • I've got all my xhr request and data handling inside the onsuccess of the indexedDB.open, have you tried it? – Ruben Teixeira Feb 19 '13 at 09:19