0

I'm trying to write a class that returns methods to work with indexedDB (I need a reusable API to share with the rest of my web app).

This is what my constructor looks like so far:

const IndexDbParams = {
  // Update this when changing the db schema
  dbVersion: 1,
  // Database, object store names
  databaseName: "CropViewData",
  fieldsObjectStoreName: "fieldsData",
  filedObjectStoreKeyName: "key",
  zoneMapObjectStoreName: "zoneMapData"
};

class IndexDbClient {
  constructor() {
    this.initializeDB().then(db => (this.db = db));
  }

  async initializeDB() {
    return new Promise((resolve, reject) => {
      const {
        dbVersion,
        databaseName,
        fieldsObjectStoreName,
        filedObjectStoreKeyName
      } = IndexDbParams;
      // Open a connection to indexDB
      const DbOpenRequest = window.indexedDB.open(databaseName, dbVersion);
      DbOpenRequest.onsuccess = e => {
        const db = DbOpenRequest.result;
        // Create data stores if none exist
        if (db.objectStoreNames.length < 1) {
          if (db.objectStoreNames.indexOf(fieldsObjectStoreName) < 0) {
            db.createObjectStore(fieldsObjectStoreName, {
              keyPath: filedObjectStoreKeyName
            });
          }
        }
        // return db object, will come hore from onupgradeneeded as well
        resolve(db);
      };
      // If we need to upgrade db version
      DbOpenRequest.onupgradeneeded = e => {
        const db = event.target.result;
        const objectStore = db.createObjectStore(fieldsObjectStoreName, {
          keyPath: filedObjectStoreKeyName
        });
      };
    });
  }

  getFieldData(compareFunction, queryParams) {
    return new Promise((resolve, reject) => {
      const { fieldsObjectStoreName } = IndexDbParams;
      const transaction = this.db.transaction(
        fieldsObjectStoreName,
        "readonly"
      );
      // If db transaction fails, reject
      transaction.onerror = e => {
        reject(transaction.error);
      };
      const objectStore = transaction.objectStore(fieldsObjectStoreName);
      const cursor = objectStore.openCursor();
      // const to store query results
      const results = [];
      cursor.onsuccess = event => {
        const result = cursor.result;
        // If we have a entry
        if (result) {
          // evaluate function here
          if (compareFunction(queryParams, result)) {
            // Add the entry to the results array
            results.push(result.value);
          }
          // If we have no more entries
        } else {
          // Return all results
          resolve(results);
        }
      };
      cursor.onerror = event => {
        reject(cursor.error);
      };
    });
  }

  getFieldById(fieldId) {
    return new Promise((resolve, reject) => {
      this.getFieldData(this.byId, { id: fieldId })
        .then(data => {
          resolve(data);
        })
        .catch(e => {
          reject(e);
        });
    });
  }

  byId({ id }, field) {
    return field.id === id;
  }

}

export default IndexDbClient;

Then I am importing the client in another file (my store):

import IndexDbClient from "../facades/IndexDbClient";

const db = new IndexDbClient();

db.getFieldById("fe276ead-def3-47a6-aefd-020e844774af").then(res => {
  console.log(res);
});

And I get an error:

Uncaught (in promise) TypeError: Cannot read property 'transaction' of undefined

If I run the same function from the console, the error is not there. I assume that this is due to the constructor not assigning the db object to the IndexDbClient yet? Is there a way for me to work around this - The way I see it I have to wait for the .onsucess event in the initializeDB() method in order to be sure the indexedDB connection is open?

Miha Šušteršič
  • 9,742
  • 25
  • 92
  • 163
  • Where is `getFieldBy` defined? Apparently it doesn't wait for the initialisation promise. – Bergi Sep 27 '17 at 17:14
  • [Don't put a promise call in your constructor](https://stackoverflow.com/q/24398699/1048572). – Bergi Sep 27 '17 at 17:15
  • Sorry about that, just a sec - I'll edit out the code – Miha Šušteršič Sep 27 '17 at 17:17
  • Thanks. I hope the duplicate linked does explain and solve your problems? – Bergi Sep 27 '17 at 17:35
  • Btw, avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it) in `getFieldById`! – Bergi Sep 27 '17 at 17:35
  • Yeh, the links outlined the problem. I have no idea how they help to solve my problem though, but thanks for trying – Miha Šušteršič Sep 27 '17 at 17:44
  • Your `IndexDbClient` should take the `db` as a constructor parameter, instead of trying to initialise it within (which it cannot do synchronously). Make `initialiseDB` a static method which returns a promise for a new, immediately usable instance. – Bergi Sep 27 '17 at 17:47
  • And then what, create a new instance of IndexDbClient, passing it the static initialiseDB method? – Miha Šušteršič Sep 27 '17 at 17:56
  • No, then use `IndexDbClient.initialiseDB().then(client => client.getFieldById(…).…)` – Bergi Sep 27 '17 at 17:58

0 Answers0