Laurens
Laurens

Reputation: 2088

IndexedDB as Angular service

I am opening an IndexedDB inside of an Angular service:

MyApp.factory '$database', ->
  database = null

  request = indexedDB.open 'myApp', '1'

  request.onerror = (e) ->
    console.log e

  request.onsuccess = (e) ->
    database = e.target.result

The service also has a method to return all records within a certain collection:

all: (collection, resultsCallback) ->
  request = database.transaction... (omitted for brevity)

The problem here is that when my page loads, a controller will fetch all records from the database. However, it is possible that the success callback of the IndexedDB has not yet been called and therefore the database is null.

Looking at the code over at https://github.com/webcss/angular-indexedDB/blob/master/src/indexeddb.js it seems the database is opened again on every query, and the query is executed in the success callback.

I am not sure if this is entirely appropriate. While this would solve the problem I am having, wouldn't this leave a lot of dangling connections to the database? Is it ok to open a connection every time a query needs to be executed?

If not, what would an appropriate solution be within Angular?

Upvotes: 1

Views: 4936

Answers (1)

Joel Jeske
Joel Jeske

Reputation: 1647

I came upon your question as I was searching for an AngularJS Wrapper for IndexedDB. I am looking through the code, which of course may have changed since you looked at it a couple months ago.

I do not believe that the wrapper you are referencing opens and closes the DB on each successive call. You might notice that every DB access calls the function this.internalObjectStore()

internalObjectStore: function(storeName, mode) {
    var me = this;
    return dbPromise().then(function(db){
        me.transaction = db.transaction([storeName], mode || READONLY);
        me.transaction.oncomplete = module.onTransactionComplete;
        me.transaction.onabort = module.onTransactionAbort;
        me.onerror = module.onTransactionError;

        return me.transaction.objectStore(storeName);
     });
},

The call to dbPromise() can either open the database or if it is already open, it simply returns the promise for the database to be opened. If the database is opened already, the promise is already resolved so the function is called immediately. This is simply a way to make sure all requests wait until the database is initially open.

Now more about your question

I believe you are experiencing your callback function being called outside an AngularJS context. Similar to how $apply() can put your code back into angular, you need a way to tell AngularJS about the code that is happening in request.onsuccess() and request.onerror(). The wrapper that you linked to uses AngularJS promises from $q which puts your code back in AngularJS context.

I am not good with this syntax so I am going to revert to standard JavaScript. What this is doing is it is running all API functions through the database promise. That will ensure that all requests will occur after the database has been opened. Also it is important to note the $apply call where the promise is resolved. That puts the context back in angular.

MyApp.factory('$database', function($q, $rootScope){
  var database = null,
      deferred = $q.defer(),
      dbPromise = deferred.promise,
      request = indexedDB.open('myApp', '1');

  request.onerror = function(e){
      console.log(e);
      $rootScope.$apply(function(){                  
          deferred.reject(e);
      });
  }

  request.onsuccess = function(e){
      $rootScope.$apply(function(){                  
          database = e.target.result;
          deferred.resolve();
      });
  }


  var api = {
      all: function(){ dbPromise.then(function(){...}); },
  }

  return api;

});

Upvotes: 3

Related Questions