はじめに

前回まで、モダンな JavaScript の書き方と、その延長として idb ライブラリを利用した IndexedDB の使い方について説明してきました。

今回は、実際に、 idb ライブラリを使ったプログラムを作成し、動作確認を行っていきたいと思います。

できるだけ、IndexedDB の標準 API を利用した古いコードと、idb ライブラリを利用したモダンな JavaScript の書き方の違いが比較できるように、 ここでは既存のプログラムを編集する形ですすめたいと思います。

具体的には、過去記事、「UI部品の作成/拡張tableタグの作成③」のコードを使用します。


テストプログラムの動作イメージの確認

コードの編集を行っていく前に、実際のプログラムの動きを確認していただこうと思います。

下記のボタンを押下することで、idb ライブラリを使用したテストプログラムの起動が可能です。

プログラムの使い方については、過去記事、「UI部品の作成/拡張tableタグの作成①」に記載がありますので、そちらを参照してください。

👉 標準 API を利用したプログラムと全く同じ動作をすることがわかると思います。


編集ポイント

プログラムの編集ポイントは下記の 2 点です。

  1. idb ライブラリの読み込み
  2. 標準 API から idb ライブラリ への変更

それでは、順を追って、変更を行っていきたいと思います。


1. idb ライブラリの読み込み

まず、idb ライブラリを読み込むよう変更していきます。対象となるファイルは、

  • index.html
  • script.js

index.html の変更

script.js で、import / export が使用できるように script.js の読み込み部分を変更します。

             :
<head>
             :
  <script type="text/javascript" src="./script.js"></script>
</head>
             :

type="text/javascript"type="module" に変更します。👇

             :
<head>
             :
  <script type="module" src="./script.js"></script>
</head>
             :

script.js の変更

scripts.js の先頭に、idb ライブラリの読み込みを追加。

//
// IndexedDBの動作確認用JavaScriptコード
//
             :

idb ライブラリを import します。👇

//
// idb ライブラリの動作確認用JavaScriptコード
//
import { openDB } from "https://cdn.jsdelivr.net/npm/idb@8/+esm";

             :

👉 以上で、idb ライブラリの読み込みは終了です。


2. 標準 API から idb ライブラリ への変更

対照となるファイルは、

  • script.js

のみです。

変更が必要な個所は、下記の 4 点になります。

  • データベースオープン処理の変更
  • 一致検索、前方一致検索ボタン押下時に、IndexedDBから値を取得し、テーブルに表示する処理の変更
  • 下限検索、上限検索、範囲検索ボタン押下時に、IndexedDBから値を取得し、テーブルに表示する処理の変更
  • カスタムイベント発生時、IndexedDBから全データ取得し、テーブルを再表示する処理の変更

データベースオープン処理の変更

idb ライブラリを使って、データベースのオープン処理を書き直します。

window.addEventListener("load", function() {
             :
  const request = indexedDB.open('SalesDatabase', 1);

  request.onupgradeneeded = (event) => {
    db = event.target.result;
    if (!db.objectStoreNames.contains("sales")) {
      const store = db.createObjectStore("sales", { keyPath: "id" });
      store.createIndex("hizukeIndex", "hizuke");
      store.createIndex("hinmokuIndex", "hinmoku");
      store.createIndex("kingakuIndex", "kingaku");
      datas.forEach(data => {
        store.add(data);
      });
    }
  };

  request.onsuccess = (event) => {
    db = event.target.result;
    document.dispatchEvent(new CustomEvent('reloadTable', {
      detail: { storename: 'sales' }
    }));
    input_value.disabled=false;
    input_lower_value.disabled=false;
    input_upper_value.disabled=false;
    btn_all.disabled=false;
    status.textContent = "";
  };
});

load イベントの関数に async を付加し、await が実行できるようにする。 その後、標準 API の indexedDB.open() を idb ライブラリ の openDB() に置き換えます。👇

window.addEventListener("load", async function() {
             :
  db = await openDB("SalesDatabase", 1, {
    upgrade(db, oldVersion, newVersion, transaction, event) {
      if (!db.objectStoreNames.contains("sales")) {
        const store = db.createObjectStore("sales", { keyPath: "id" });
        store.createIndex("hizukeIndex", "hizuke");
        store.createIndex("hinmokuIndex", "hinmoku");
        store.createIndex("kingakuIndex", "kingaku");
        datas.forEach(data => {
          store.add(data);
        });
      }
    }
  });

  document.dispatchEvent(new CustomEvent('reloadTable', {
    detail: { storename: 'sales' }
  }));
  input_value.disabled=false;
  input_lower_value.disabled=false;
  input_upper_value.disabled=false;
  btn_all.disabled=false;
  status.textContent = "";
});


一致検索、前方一致検索ボタン押下時に、IndexedDBから値を取得し、テーブルに表示する処理の変更

idb ライブラリを使って、カーソル読み込み処理を書き直します。

             :
  // 一致検索、前方一致検索ボタン押下時に、IndexedDBから値を取得し、テーブルに表示
             :
    button.addEventListener('click', function(event) {
      const tx = db.transaction('sales', 'readonly');
      const store = tx.objectStore('sales');
             :
          switch (input_field.value) {
            case "id" : 
              request = store.openCursor(query(Number(input_value.value)));
              break;
            case "hizuke" :
              request = store.index(input_field.value + "Index").openCursor(query(new Date(input_value.value)));
              break;
            case "hinmoku" :
              request = store.index(input_field.value + "Index").openCursor(query(input_value.value));
              break;
            case "kingaku" :
              request = store.index(input_field.value + "Index").openCursor(query(Number(input_value.value)));
              break;
            default :
              request = store.openCursor(query(0));
              break;
          }
             :
      request.onsuccess = (event) => {
        const cursor = event.target.result;
        if (!cursor) {
          table.setFooter();
          if (!results.length) status.textContent = "指定されたデータがありません。";
          return;
        }

        results.push(cursor.value)
        table.setData(cursor.value);

        status.textContent = "完了"

        if (results.length < MAX_COUNT) {
          cursor.continue();
          return;
        }

        table.setFooter();
      }

      request.onerror = () => {
        status.textContent = "エラー発生"
      }
    });
             :

click イベントの関数に async を付加し、await が実行できるようにする。 その後、標準 API の cursor ループ処理を idb ライブラリ の非同期イテレーター処理に置き換えます。👇

             :
  // 一致検索、前方一致検索ボタン押下時に、IndexedDBから値を取得し、テーブルに表示
  [btn_only, btn_prefix_match].forEach(function (button) {
    button.addEventListener('click', async function(event) {
      const tx = db.transaction('sales');
             :
          switch (input_field.value) {
            case "id" : 
              request = tx.store.iterate(query(Number(input_value.value)));
              break;
            case "hizuke" :
              request = tx.store.index(input_field.value + "Index").iterate(query(new Date(input_value.value)));
              break;
            case "hinmoku" :
              request = tx.store.index(input_field.value + "Index").iterate(query(input_value.value));
              break;
            case "kingaku" :
              request = tx.store.index(input_field.value + "Index").iterate(query(Number(input_value.value)));
              break;
            default :
              request = tx.store.iterate(query(0));
              break;
          }
             :
      tx.done.catch(err => {
        status.textContent = `トランザクションエラー : ${err}`;
      });

      for await (const cursor of request) {
        if (results.length >= MAX_COUNT) break;

        results.push(cursor.value)
        table.setData(cursor.value);
      }

      table.setFooter();

      if (!results.length) {
        status.textContent = "指定されたデータがありません。";
      } else {
        status.textContent = "完了"
      }
    });
  });
             :


下限検索、上限検索、範囲検索ボタン押下時に、IndexedDBから値を取得し、テーブルに表示する処理の変更

idb ライブラリを使って、カーソル読み込み処理を書き直します。

             :
  // 下限検索、上限検索、範囲検索ボタン押下時に、IndexedDBから値を取得し、テーブルに表示
             :
    button.addEventListener('click', function(event) {
      const tx = db.transaction('sales', 'readonly');
      const store = tx.objectStore('sales');
             :
          switch (input_field.value) {
            case "id" : 
              request = store.openCursor(query(Number(input_lower_value.value), Number(input_upper_value.value), lower_open, upper_open));
              break;
            case "hizuke" :
              request = store.index(input_field.value + "Index").openCursor(query(new Date(input_lower_value.value), new Date(input_upper_value.value), lower_open, upper_open));
              break;
            case "hinmoku" :
              request = store.index(input_field.value + "Index").openCursor(query(input_lower_value.value, input_upper_value.value, lower_open, upper_open));
              break;
            case "kingaku" :
              request = store.index(input_field.value + "Index").openCursor(query(Number(input_lower_value.value), Number(input_upper_value.value), lower_open, upper_open));
              break;
            default :
              request = store.openCursor(query(0, 0, 0, 0));
              break;
          }
             :
      request.onsuccess = (event) => {
        const cursor = event.target.result;
        if (!cursor) {
          table.setFooter();
          if (!results.length) status.textContent = "指定されたデータがありません。";
          return;
        }

        results.push(cursor.value)
        table.setData(cursor.value);

        status.textContent = "完了"

        if (results.length < MAX_COUNT) {
          cursor.continue();
          return;
        }

        table.setFooter();
      }

      request.onerror = () => {
        status.textContent = "エラー発生"
      }
    });
             :

click イベントの関数に async を付加し、await が実行できるようにする。 その後、標準 API の cursor ループ処理を idb ライブラリ の非同期イテレーター処理に置き換えます。👇

             :
  // 下限検索、上限検索、範囲検索ボタン押下時に、IndexedDBから値を取得し、テーブルに表示
             :
    button.addEventListener('click', async function(event) {
      const tx = db.transaction('sales');
             :
          switch (input_field.value) {
            case "id" : 
              request = tx.store.iterate(query(Number(input_lower_value.value), Number(input_upper_value.value), lower_open, upper_open));
              break;
            case "hizuke" :
              request = tx.store.index(input_field.value + "Index").iterate(query(new Date(input_lower_value.value), new Date(input_upper_value.value), lower_open, upper_open));
              break;
            case "hinmoku" :
              request = tx.store.index(input_field.value + "Index").iterate(query(input_lower_value.value, input_upper_value.value, lower_open, upper_open));
              break;
            case "kingaku" :
              request = tx.store.index(input_field.value + "Index").iterate(query(Number(input_lower_value.value), Number(input_upper_value.value), lower_open, upper_open));
              break;
            default :
              request = tx.store.iterate(query(0, 0, 0, 0));
              break;
          }
             :
      tx.done.catch(err => {
        status.textContent = `トランザクションエラー : ${err}`;
      });

      for await (const cursor of request) {
        if (results.length >= MAX_COUNT) break;

        results.push(cursor.value)
        table.setData(cursor.value);
      }

      table.setFooter();

      if (!results.length) {
        status.textContent = "指定されたデータがありません。";
      } else {
        status.textContent = "完了"
      }
    });

             :


カスタムイベント発生時、IndexedDBから全データ取得し、テーブルを再表示する処理の変更

idb ライブラリを使って、カーソル読み込み処理を書き直します。

             :
  // カスタムイベント発生時、IndexedDBから全データ取得し、テーブルを再表示
  document.addEventListener('reloadTable', (e) => {
    const tx = db.transaction(e.detail.storename, 'readonly');
    const store = tx.objectStore(e.detail.storename);
             :
    const request = store.openCursor();
    request.onsuccess = (event) => {
      const cursor = event.target.result;
      if (!cursor) {
        table.setFooter();
        return;
      }

      results.push(cursor.value)
      table.setData(cursor.value);

      if (results.length < MAX_COUNT) {
          cursor.continue();
          return;
      }

      table.setFooter();
    }
  });
             :

reloadTable イベントの関数に async を付加し、await が実行できるようにする。 その後、標準 API の cursor ループ処理を idb ライブラリ の非同期イテレーター処理に置き換えます。👇

             :
  // カスタムイベント発生時、IndexedDBから全データ取得し、テーブルを再表示
  document.addEventListener('reloadTable', async (e) => {
    const tx = db.transaction(e.detail.storename);
             :
    tx.done.catch(err => {
      status.textContent = `トランザクションエラー : ${err}`;
    });

    for await (const cursor of tx.store) {
      if (results.length >= MAX_COUNT) break;

      results.push(cursor.value)
      table.setData(cursor.value);
    }

    table.setFooter();
  });
             :

👉 以上で、標準 API から idb ライブラリ への変更は終了です。

(つづく)