2015/03/26

TypeScriptでIndexedDBの登録・更新・削除・検索をする

IndexedDBは、HTML5で動作するKey-Value型のローカルストレージ。
簡単に言うと、ブラウザを閉じても保存しておきたいデータを格納するための仕組み。

他にもWebStorageやWebSQLなどがある。
今後は、IndexedDBが最も利用されるデータベースになる! ……らしい。

当エントリは、最近個人的に勉強を始めた「TypeScript」でIndexedDBを操作する方法をまとめる。
最後にコンパイルしたJavaScriptも載せておくので、TypeScriptに興味ない人はそちらを参照ください。

開発環境

  • TypeScript 1.4
  • Visual Studio Community 2013
  • InternetExplorer 11 / Google Chrome 41



今回つくるモノ


今回IndexedDBを使うにあたり、以下のサイトを参考にさせていただいた。

▶ Indexed Database API について - へっぽこプログラマーの日記


そして、以下のような感じの超簡易的なレイアウトで、「登録」「更新」「削除」「全削除」「検索」「全検索」を行う。


開発メモ

  • データベース名:library
  • オブジェクトストア名:books
  • インデックス名:index
  • キー項目名:isbn

注意点

今回のサンプルにjQueryを使用している。
VS Community 2013インストール直後だと、jQueryの型定義でアホほどエラーがでる。
そんなときは、TypeScriptのバージョンを1.4にしてください。

▶ TypeScript 1.4 for Visual Studio 2013 拡張機能


ちなみに、jQueryや型定義は、Nugetで「jQuery DifinitelyTyped」とか検索するとでてくる。



データベースの作成


// ベンタープレフィックス(webkit:Chrome、moz:FireFox)
var indexedDB = window.indexedDB;// || window.webkitIndexedDB;

// データベース接続
var db: IDBDatabase = null;
var request = indexedDB.open('library', 1);

// 接続成功(初回 or DBのバージョンが変わったとき)
request.onupgradeneeded = (event) => {
    db = (<IDBRequest>event.target).result;

    // オブジェクトストア作成(インデックスのキーとして'isbn'を指定)
    var store = db.createObjectStore('books', { keyPath: 'isbn' });
    // インデックスの作成
    store.createIndex('index', 'isbn', false);
};

// 接続成功(すでにDBが存在する場合)
request.onsuccess = (event) => {
    db = (<IDBRequest>event.target).result;
};

request.onerror = (event) => {
    console.log(event.message);
};

ベンタープレフィックス対応のところで、window.webkitIndexedDBを使用すると、Property 'webkitIndexedDB' does not exist on type 'Window'.というエラーが発生するため、コメントアウトしている。(コメントアウトした状態でも、ChromeでIndexedDB使えたけど…)

onupgradeneededonsuccessのコールバックメソッド内で、JavaScriptならthis.resultevent.target.resultと書けるが、TypeScriptの場合はそのまま書くとエラーになってしまうため、適切な型にキャストしている。


型推論を使っているので、一見どんな型かわからないと思うので、補足しておく。
TypeScriptを使うときは、型に注意しないとエラーになっちゃうので!

変数名
indexedDBIDBFactory
dbIDBDatabase
requestIDBOpenDBRequest
storeIDBObjectStore



データ登録・更新


var trans = db.transaction('books', 'readwrite');
var store = trans.objectStore('books');

// 登録対象のデータ(BookInfoはInterfaceで定義)
var bookInfo: BookInfo = {
    name: $('#name').val(),
    description: $('#description').val(),
    author: $('#author').val(),
    isbn: $('#isbn').val()
};

// データ登録・更新
var request = store.put(bookInfo);

request.onsuccess = (event) => {
    console.log('登録成功');
};

request.onerror = (event) => {
    console.log('登録失敗:' + event.message);
};

トランザクションを開始し、データを登録・更新する処理。

13行目のstore.put(bookInfo)>だが、
IDBObjectStore.put()を使うと、キーが存在する場合は更新、存在しない場合は登録と自動的に判断してくれる。
IDBObjectStore.add()もあるが、こちらはキーが存在すると重複エラー、存在しない場合は登録となる。

似ているようでちょっと違うので要注意!


一応型も。。。
変数名
transIDBTransaction
storeIDBObjectStore
requestIDBRequest
bookInfoユーザ定義(BookInfo)



データ検索


var key = $('#isbn').val();

var trans = db.transaction('books', 'readonly');
var store = trans.objectStore('books');

// データ取得
var request = store.get(key);

request.onsuccess = (event) => {
    var result = <BookInfo>(<IDBRequest>event.target).result;
    if (result) {
        render(result);
    } else {
        console.log('対象データは存在しません。');
    }
};

1行目の「key」が検索キーをテキストボックスから取得する。
この検索キーは、オブジェクトストア作成時に「keyPath」として指定したモノ。

7行目の「store.get(key)」でデータを取得する。
データが0件だった場合も、onsuccessコールバックが呼ばれるので注意。

12行目は、取得したデータ(result:BookInfo)を画面表示用のRenderメソッドに渡している。

型については、「データ登録・更新」と同じなので省略。



全データ取得


var trans = db.transaction('books', 'readonly');
var store = trans.objectStore('books');

// 全データ取得
var request = store.openCursor();

request.onsuccess = (event) => {
    var cursor = <IDBCursorWithValue>(<IDBRequest>event.target).result;

    if (cursor) {
        render(cursor.value);
        cursor.continue();
    }
};

「データ検索」と違い、store.openCursorを使うことで一度に複数のデータを取得することができる。

openCursor(optionalKeyRange, optionalDirection)の第1引数にIDBKeyRangeを渡すことで「1件目~10件目までを取得」といった範囲指定も可能。第2引数に'NEXT'や’PREV'などを渡すことで、取得する順番も指定できる。

データ取得後の処理は、request.onsuccessが何度も呼ばれるイメージ。

transやstoreなどは、「データ登録・更新」と同じなので省略。
変数名
cursorIDBCursorWithValue



データ削除


var key = $('#isbn').val();

var trans = db.transaction('books', 'readwrite');
var store = trans.objectStore('books');

// データ削除
var request = store.delete(key);

request.onsuccess = (event) => {
    console.log('削除成功');
};

request.onerror = (event) => {
    console.log('削除失敗:' + event.message);
};
「データ検索」に似ている。
7行目の「store.delete(key)」で、データを削除する。

この削除するときのキーは、オブジェクトストア作成時に「keyPath」として指定したモノ。

型は他のと同じなので省略。



全データ削除


var trans = db.transaction('books', 'readwrite');
var store = trans.objectStore('books');

// 全データ削除
var request = store.clear();

request.onsuccess = (event) => {
    console.log('全データ削除成功');
};

request.onerror = (event) => {
    console.log('全データ削除失敗:' + event.message);
};

「データ削除」とほぼ同じ。
違うところは、5行目のstore.clear()これでオブジェクトストア内のデータがすべて消える。
※オブジェクトストアは削除されない。

型は(以下略



ソースコード(HTML+TypeScript+JavaScript)


$(function () {
var indexedDB = window.indexedDB;
var db = null;
var request = indexedDB.open('library', 1);
request.onupgradeneeded = function (event) {
db = event.target.result;
var store = db.createObjectStore('books', { keyPath: 'isbn' });
store.createIndex('index', 'isbn', false);
};
request.onsuccess = function (event) {
db = event.target.result;
};
request.onerror = function (event) {
console.log(event.message);
};
$('#regist').click(function () {
var trans = db.transaction('books', 'readwrite');
var store = trans.objectStore('books');
var bookInfo = {
name: $('#name').val(),
description: $('#description').val(),
author: $('#author').val(),
isbn: $('#isbn').val()
};
var request = store.put(bookInfo);
request.onsuccess = function (event) {
console.log('登録成功');
};
request.onerror = function (event) {
console.log('登録失敗:' + event.message);
};
});
$('#search').click(function () {
clear();
var key = $('#isbn').val();
var trans = db.transaction('books', 'readonly');
var store = trans.objectStore('books');
var request = store.get(key);
request.onsuccess = function (event) {
var result = event.target.result;
if (result) {
render(result);
}
else {
console.log('対象データは存在しません。');
}
};
});
$('#all-search').click(function () {
clear();
var trans = db.transaction('books', 'readonly');
var store = trans.objectStore('books');
var request = store.openCursor();
request.onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
render(cursor.value);
cursor.continue();
}
};
});
var render = function (bookInfo) {
var tr = $('<tr>').append('<td>' + bookInfo.name + '</td>' + '<td>' + bookInfo.description + '</td>' + '<td>' + bookInfo.author + '</td>' + '<td>' + bookInfo.isbn + '</td>');
$('#lists > tbody').append(tr);
};
var clear = function () {
$('#lists > tbody > tr').remove();
};
$('#delete').click(function () {
var key = $('#isbn').val();
var trans = db.transaction('books', 'readwrite');
var store = trans.objectStore('books');
var request = store.delete(key);
request.onsuccess = function (event) {
console.log('削除成功');
};
request.onerror = function (event) {
console.log('削除失敗:' + event.message);
};
});
$('#all-delete').click(function () {
var trans = db.transaction('books', 'readwrite');
var store = trans.objectStore('books');
var request = store.clear();
request.onsuccess = function (event) {
console.log('全データ削除成功');
};
request.onerror = function (event) {
console.log('全データ削除失敗:' + event.message);
};
});
});
//# sourceMappingURL=app.js.map
view raw app.js hosted with ❤ by GitHub
/// <reference path="scripts/typings/jquery/jquery.d.ts" />
// データベースに登録するオブジェクトの型
interface BookInfo {
name: string;
description: string;
author: string;
isbn: number;
}
$(() => {
// ベンタープレフィックス(webkit:Chrome、moz:FireFox)
var indexedDB = window.indexedDB; // || window.webkitIndexedDB;
// データベース接続
var db: IDBDatabase = null;
var request = indexedDB.open('library', 1);
// 接続成功(初回 or DBのバージョンが変わったとき)
request.onupgradeneeded = (event) => {
db = (<IDBRequest>event.target).result;
// オブジェクトストア作成(インデックスのキーとして'isbn'を指定)
var store = db.createObjectStore('books', { keyPath: 'isbn' });
// インデックスの作成
store.createIndex('index', 'isbn', false);
};
// 接続成功(すでにDBが存在する場合)
request.onsuccess = (event) => {
db = (<IDBRequest>event.target).result;
};
request.onerror = (event) => {
console.log(event.message);
};
// データ登録・更新
$('#regist').click(() => {
var trans = db.transaction('books', 'readwrite');
var store = trans.objectStore('books');
// 登録対象のデータ(BookInfoはInterfaceで定義)
var bookInfo: BookInfo = {
name: $('#name').val(),
description: $('#description').val(),
author: $('#author').val(),
isbn: $('#isbn').val()
};
// データ登録・更新
var request = store.put(bookInfo);
request.onsuccess = (event) => {
console.log('登録成功');
};
request.onerror = (event) => {
console.log('登録失敗:' + event.message);
};
});
// データ検索
$('#search').click(() => {
clear();
var key = $('#isbn').val();
var trans = db.transaction('books', 'readonly');
var store = trans.objectStore('books');
// データ取得
var request = store.get(key);
request.onsuccess = (event) => {
var result = <BookInfo>(<IDBRequest>event.target).result;
if (result) {
render(result);
} else {
console.log('対象データは存在しません。');
}
};
});
// 全データ検索
$('#all-search').click(() => {
clear();
var trans = db.transaction('books', 'readonly');
var store = trans.objectStore('books');
// 全データ取得
var request = store.openCursor();
request.onsuccess = (event) => {
var cursor = <IDBCursorWithValue>(<IDBRequest>event.target).result;
if (cursor) {
render(cursor.value);
cursor.continue();
}
};
});
// 画面表示
var render = (bookInfo: BookInfo) => {
var tr = $('<tr>').append(
'<td>' + bookInfo.name + '</td>' +
'<td>' + bookInfo.description + '</td>' +
'<td>' + bookInfo.author + '</td>' +
'<td>' + bookInfo.isbn + '</td>'
);
$('#lists > tbody').append(tr);
};
// 画面クリア
var clear = () => {
$('#lists > tbody > tr').remove();
};
// データ削除
$('#delete').click(() => {
var key = $('#isbn').val();
var trans = db.transaction('books', 'readwrite');
var store = trans.objectStore('books');
// データ削除
var request = store.delete(key);
request.onsuccess = (event) => {
console.log('削除成功');
};
request.onerror = (event) => {
console.log('削除失敗:' + event.message);
};
});
// 全データ削除
$('#all-delete').click(() => {
var trans = db.transaction('books', 'readwrite');
var store = trans.objectStore('books');
// 全データ削除
var request = store.clear();
request.onsuccess = (event) => {
console.log('全データ削除成功');
};
request.onerror = (event) => {
console.log('全データ削除失敗:' + event.message);
};
});
});
view raw app.ts hosted with ❤ by GitHub
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Indexed Database API</title>
</head>
<body>
<h1>IndexedDB Sample</h1>
ISBN(*):<input type="text" id="isbn" /><br />
名前: <input type="text" id="name" /><br />
詳細:<input type="text" id="description" /><br />
著者:<input type="text" id="author" /><br />
<button type="button" id="regist">登録</button>
<button type="button" id="search">検索</button>
<button type="button" id="all-search">全検索</button>
<button type="button" id="delete">削除</button>
<button type="button" id="all-delete">全削除</button>
<hr />
<table id="lists" border="1">
<thead>
<tr>
<th>名前</th>
<th>詳細</th>
<th>著者</th>
<th>ISBN</th>
</tr>
</thead>
<tbody></tbody>
</table>
<script src="Scripts/jquery-2.1.3.min.js"></script>
<script src="app.js"></script>
</body>
</html>
view raw index.html hosted with ❤ by GitHub


参考サイト


わからないところは、MDNで型(IDBObjectStoreなど)を検索し、「Example」を入ればだいたいわかるのでオススメ。



以上

written by @bc_rikko

0 件のコメント :

コメントを投稿