Pada web konvensional, satu-satunya metode formal untuk menyimpan data di browser adalah dengan menggunakan cookie. JavaScript dapat mengakses isi cookie melalui document.cookie. HTML5 menawarkan banyak cara baru untuk menyimpan data di sisi client. Saya bisa menyimpan data dengan mengasosiasikannya pada sebuah elemen HTML melalui attribute HTLM5 Custom Data Attributes (data-*). Selain itu, HTML5 juga menawarkan penyimpanan data melalui window.localStorage atau window.sessionStorage. Berbeda dengan penyimpanan melalui cookie, penggunaan HTML5 Web Storage sepenuhnya terjadi di sisi client dan tidak perlu di-inisialisasi melalui HTTP header dari server. Untuk penyimpanan data yang lebih banyak lagi, browser HTML5 memiliki database internal yang dapat diakses melalui Indexed Database API (http://www.w3.org/TR/IndexedDB/).

Pada artikel ini, saya akan mencoba memakai IndexedDB API dengan JavaScript. Sebagai latihan, saya akan membuat sebuah HTML sederhana dimana pengguna dapat mengisi data, menampilkannya pada tabel, dan menyimpannya pada database lokal di browser melalui IndexedDB API. Saya akan mulai dengan membuat HTML seperti berikut ini:

<!DOCTYPE html>
<html manifest="latihan.appcache">
<head lang="en">
  <meta charset="UTF-8">
  <title>Latihan IndexedDB</title>
  <style type='text/css'>
  table {
     font: 17px/30px Verdana, Arial, Helvetica, sans-serif;
     border-collapse: collapse; width: 520px;
  }
  table th {
     padding: 0 0.5em; text-align: left; background-color: #FFE45C;
  }
  table tr {
     border-top: 1px solid #fb7a31; border-bottom: 1px solid #fb7a31;
     background: #ffc;
  }
  table td {
     border-bottom: 1px solid #ccc; padding: 0 0.5em;
  }
  form {
     margin-top: 30px;
  }
  ul {
     list-style-type: none; margin: 0px; padding: 0px;
  }
  li {
     padding: 12px; border-bottom: 1px solid #eee;
  }
  li:first-child, li:last-child {
     border-top: 1px solid #777;
  }
  label {
     width: 150px; float: left; margin-top: 3px;
     display: inline-block;
  }
  #pesan {
     border: 1px dashed #eee; padding: 10px;
     color: #808080; font-family: monospace;
  }
 </style>
 </head>
 <body>
       <table>
              <thead>
                     <tr>
                         <th>NIM</th>
                         <th>Nama</th>
                         <th>Tanggal Lahir</th>
                         <th>IPK</th>
                         <th>Aksi</th>
                     </tr>
              </thead>
              <tbody id="tabel">
              </tbody>
      </table>
                     <form id='frmUtama'>
                         <ul>
                             <li>
                                 <label for="nim">NIM:</label>
                                 <input id="nim" name="nim" type="text" maxlength="20" required/>
                            </li>
                             <li>
                                 <label for="nama">Nama:</label>
                                 <input id="nama" name="nama" type="text" maxlength="50" required/>
                            </li>
                             <li>
                                 <label for="tanggalLahir">Tanggal Lahir:</label>
                                 <input id="tanggalLahir" name="tanggalLahir" type="text" maxlength="20" placeholder="01/01/1991" pattern="[0-9]{2}/[0-9]{2}/[0-9]{4}" required/>
                            </li>
                             <li>
                                 <label for="ipk">IPK:</label>
                                 <input id="ipk" name="ipk" type="number" maxlength="3" step="0.1" required />
                            </li>
                             <li>
                                 <input id='btnTambah' type="submit" value="Tambah" />
                            </li>
                       </ul>
                   </form>

var tabel = document.getElementById(‘tabel’), nim = document.getElementById(‘nim’), nama = document.getElementById(‘nama’), tanggalLahir = document.getElementById(‘tanggalLahir’), btnTambah = document.getElementById(‘btnTambah’), form = document.getElementById(‘frmUtama’), pesan = document.getElementById(‘pesan’), ipk = document.getElementById(‘ipk’), db; function tambahBaris(e) { // Periksa apakah NIM sudah ada if (tabel.rows.namedItem(nim.value)) { pesan.textContent = ‘Error: Nim sudah terdaftar!’; e.preventDefault(); return; } // Membuat baris baru var baris = tabel.insertRow(); baris.id = nim.value; baris.insertCell().appendChild(document.createTextNode(nim.value)); baris.insertCell().appendChild(document.createTextNode(nama.value)); baris.insertCell().appendChild(document.createTextNode(tanggalLahir.value)); baris.insertCell().appendChild(document.createTextNode(ipk.value)); // Membuat tombol hapus untuk setiap baris var btnHapus = document.createElement(‘input’); btnHapus.type = ‘button’; btnHapus.value = ‘Hapus’; btnHapus.id = nim.value; baris.insertCell().appendChild(btnHapus); e.preventDefault(); } form.addEventListener(‘submit’, tambahBaris, false); tabel.addEventListener(‘click’, hapusBaris, true); </html>

Pada HTML di atas, saya dapat menambah dan menghapus baris dari tabel seperti yang terlihat pada gambar berikut ini:

error

Pada gambar di atas, terlihat bahwa pengguna tidak boleh menambahkan mahasiswa dengan NIM yang sama karena saya akan memakai NIM sebagai key atau primary key yang bersifat unik.

Sekarang, saya akan mencoba menyimpan setiap mahasiswa yang ditambahkan oleh pengguna ke dalam database lokal. Tapi sebelumnya, langkah paling awal yang perlu saya lakukan adalah membuat database baru. Untuk itu, saya menambahkan kode program JavaScript seperti berikut ini:

function kesalahanHandler(e) {
    pesan.innerHTML += 'Kesalahan Database: ' + e.target.errorCode + '<br>';
}

function buatDatabase() {
    var request = window.indexedDB.open('latihan', 1);
        request.onerror = kesalahanHandler;
        request.onupgradeneeded = function(e) {
            var db = e.target.result;
            db.onerror = kesalahanHandler;
            var objectstore = db.createObjectStore('mahasiswa', { keyPath: 'nim' });
            pesan.innerHTML += 'Object store mahasiswa berhasil dibuat.<br>';
        }
        request.onsuccess = function(e) {
            db = e.target.result;
            db.onerror = kesalahanHandler;
            pesan.innerHTML += 'Berhasil melakukan koneksi ke database!<br>';
            
        }
}

Pemanggilan IndexedDB API selalu bersifat asynchronous sehingga saya harus memakai event handler. Sebagai contoh window.indexedDB.open() akan melakukan koneksi ke database. Tapi ia tidak melakukannya saat itu juga. Ia hanya mengembalikan sebuah IDBRequest. Lalu kapan saya tahu bahwa ‘request ‘ saya sudah selesai dikerjakan? Event success untuk IDBRequest menandakan bahwa ‘request’ tersebut sudah selesai dikerjakan.

Selain itu, bila ini adalah pertama kali database dibuat atau database memiliki versi yang lebih rendah dari yang hendak dipakai, maka event onupgradeneeded untuk IDBRequest akan terjadi. Ini adalah saat yang tepat untuk membuat object store baru. Perlu diperhatikan bahwa IndexedDB API tidak menyimpan data dalam bentuk tabel seperti pada database relasional. Data (tepatnya object) disimpan langsung ke object store berdasarkan sebuah key. Object store adalah sesuatu yang menyerupai tabel di database relasional.

Pada Firefox, data yang dimanipulasi melalui IndexedDB API akan disimpan melalui database SQLite (database relasional). Firefox juga menggunakan SQLite untuk menyimpan nilai penting lain miliknya. Browser yang berbeda bisa saja menggunakan back-end yang berbeda. Misalnya, pada Google Chrome, back-end untuk IndexedDB API adalah LevelDB (database NoSQL) buatan Google.

Install SQLite terlebih dahulu :

$ sudo ap-get update
$ sudo apt-get install sqlite3 libsqlite3-dev

Programmer JavaScript yang memakai IndexedDB API tidak perlu (dan tidak seharusnya) mengetahui back-end database karena tujuan dari dari IndexedDB API adalah sebuah abstraksi yang mempermudah developer. Saya membuka isi database back-end hanya untuk menunjukkan bahwa object store yang dibuat dengan IndexedDB API berhasil disimpan oleh Firefox dalam bentuk sebuah database SQLite.

Berikutnya, saya akan menambahkan data pada object store bila pengguna membuat sebuah baris baru di tabel. Untuk itu, saya membuat JavaScript berikut ini:

function cetakPesanHandler(msg) {
    return function(e) {
       pesan.innerHTML += msg + '<br>';
    }
}

function buatTransaksi() {
    var transaction = db.transaction(['mahasiswa'], 'readwrite');
    transaction.onerror = kesalahanHandler;
    transaction.oncomplete = cetakPesanHandler('Transaksi baru saja diselesaikan.');
    return transaction;
}

function tambahKeDatabase(mahasiswa) {
    var objectstore = buatTransaksi().objectStore('mahasiswa');
    var request = objectstore.add(mahasiswa);
    request.onerror = kesalahanHandler;
    request.onsuccess = cetakPesanHandler('Mahasiswa [' + mahasiswa.nim + '] telah ditambahkan ke database lokal.');
}

function tambahBaris(e) {
....

   // Tambah ke database
   tambahKeDatabase({
     nim: nim.value,
     nama: nama.value,
     tanggalLahir: tanggalLahir.value,
     ipk: ipk.value
   });

}

Pada kode program di atas, setiap kali menambah baris baru ke tabel, saya akan memanggiltambahKeDatabase() yang akan membuat sebuah IDBTransaction baru. Untuk menambah data ke object store, saya perlu memanggil method IDBTransaction.objectStore() yang akan mengambalikan object store dalam bentuk IDBObjectStore. Setelah itu, saya bisa memanipulasi object dalam object store dengan memanggil method IDBObjectStore seperti add(), clear(), delete(), put() dan sebagainya. Pada contoh di atas, karena saya ingin menambahkan data baru, maka saya memanggil IDBObjectStore.add().

Konsep transaction pada IndexedDB API cukup berbeda dibandingkan dengan database pada umumnya. Transaction disini bersifat asynchronous dan akan di-commit secara otomatis oleh browser. Developer hanya bisa membatalkannya dengan memanggil method abort(). Lalu kapan transaction di-commit? Selama browser melihat ada request yang diberikan pada transaction, maka transaction akan tetap aktif. Setelah tidak ada request untuk transaction tersebut, browser akan men-commit transaction tersebut secara otomatis. Perilaku ini disebut sebagaiauto-commit. Bila user menutup browser sebelum browser sempat melakukan auto-commit, maka perubahan pada transaction tersebut tidak akan disimpan.

Sampai disini, setiap baris yang saya tambahkan ke tabel akan ikut tersimpan ke database seperti yang terlihat pada gambar berikut ini:

gakerror

Setelah berhasil menyimpan data, saya akan kembali melakukan perubahan pada JavaScript agar membaca isiobject store pada saat halaman pertama kali dibuka. Untuk itu, saya menambahkan kode program JavaScript berikut ini:

function buatDatabase() {
...
 request.onsuccess = function(e) {
 ...
   bacaDariDatabase();
 }
}

function bacaDariDatabase() {
   var objectstore = buatTransaksi().objectStore('mahasiswa');
   objectstore.openCursor().onsuccess = function(e) {
   var result = e.target.result;
   if (result) {
      pesan.innerHTML += 'Membaca mahasiswa [' + result.value.nim + '] dari database.<br>';
      var baris = tabel.insertRow();
      baris.id = result.value.nim;
      baris.insertCell().appendChild(document.createTextNode(result.value.nim));
      baris.insertCell().appendChild(document.createTextNode(result.value.nama));
      baris.insertCell().appendChild(document.createTextNode(result.value.tanggalLahir));
      baris.insertCell().appendChild(document.createTextNode(result.value.ipk));
      var btnHapus = document.createElement('input');
      btnHapus.type = 'button';
      btnHapus.value = 'Hapus';
      btnHapus.id = result.value.nim;
      baris.insertCell().appendChild(btnHapus);
      result.continue();
  }
}

Pada kode program di atas, untuk membaca seluruh isi object store, saya menggunakan cursor dengan memanggilopenCursor(). Untuk melakukan iterasi ke seluruh object yang ada, saya harus memanggil method continue()dari IDBCursor yang dikembalikan. Pemanggilan continue() akan menyebabkan event handler untuk successkembali dipanggil. Bila masih ada object yang bisa dikembalikan, saya dapat membaca nilainya yang ditampung dalam property result. Bila sudah tidak ada object lagi, maka result akan bernilai undefined.

Sekarang, bila saya menutup browser lalu membuka browser lagi, baris yang sama seperti sebelumnya akan tetap muncul kembali.

Sebagai langkah terakhir, saya perlu menghapus nilai dari database lokal bila seandainya baris dihapus oleh pengguna. Oleh sebab itu, saya menambahkan kode program JavaScript berikut ini:

function hapusBaris(e) {
   if (e.target.type=='button') {
       tabel.deleteRow(tabel.rows.namedItem(e.target.id).sectionRowIndex);
       hapusDariDatabase(e.target.id);
   }
}

function hapusDariDatabase(nim) {
   var objectstore = buatTransaksi().objectStore('mahasiswa');
   var request = objectstore.delete(nim);
       request.onerror = kesalahanHandler;
       request.onsuccess = cetakPesanHandler('Mahasiswa [' + nim + '] berhasil dihapus dari database lokal.');
}

Selesai…….. 

Keep Learn, Have fun Coding!!!

Advertisements