JavaScript dan IndexedDB
Dalam artikel ini, kami akan menjelaskan JavaScript dan IndexedDB.
Tutorial ini memberikan penjelasan langkah-demi-langkah tentang JavaScript dan IndexedDB
, termasuk kode contoh praktis di setiap langkah untuk membantu memperdalam pemahaman Anda.
YouTube Video
javascript-indexed-db.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>JavaScript & HTML</title>
6 <style>
7 * {
8 box-sizing: border-box;
9 }
10
11 body {
12 margin: 0;
13 padding: 1em;
14 padding-bottom: 10em;
15 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16 background-color: #f7f9fc;
17 color: #333;
18 line-height: 1.6;
19 }
20
21 .container {
22 max-width: 800px;
23 margin: 0 auto;
24 padding: 1em;
25 background-color: #ffffff;
26 border: 1px solid #ccc;
27 border-radius: 10px;
28 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
29 }
30
31 .container-flex {
32 display: flex;
33 flex-wrap: wrap;
34 gap: 2em;
35 max-width: 1000px;
36 margin: 0 auto;
37 padding: 1em;
38 background-color: #ffffff;
39 border: 1px solid #ccc;
40 border-radius: 10px;
41 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
42 }
43
44 .left-column, .right-column {
45 flex: 1 1 200px;
46 min-width: 200px;
47 }
48
49 h1, h2 {
50 font-size: 1.2rem;
51 color: #007bff;
52 margin-top: 0.5em;
53 margin-bottom: 0.5em;
54 border-left: 5px solid #007bff;
55 padding-left: 0.6em;
56 background-color: #e9f2ff;
57 }
58
59 button {
60 display: block;
61 margin: 1em auto;
62 padding: 0.75em 1.5em;
63 font-size: 1rem;
64 background-color: #007bff;
65 color: white;
66 border: none;
67 border-radius: 6px;
68 cursor: pointer;
69 transition: background-color 0.3s ease;
70 }
71
72 button:hover {
73 background-color: #0056b3;
74 }
75
76 #output {
77 margin-top: 1em;
78 background-color: #1e1e1e;
79 color: #0f0;
80 padding: 1em;
81 border-radius: 8px;
82 min-height: 200px;
83 font-family: Consolas, monospace;
84 font-size: 0.95rem;
85 overflow-y: auto;
86 white-space: pre-wrap;
87 }
88
89 .highlight {
90 outline: 3px solid #ffc107; /* yellow border */
91 background-color: #fff8e1; /* soft yellow background */
92 transition: background-color 0.3s ease, outline 0.3s ease;
93 }
94
95 .active {
96 background-color: #28a745; /* green background */
97 color: #fff;
98 box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
99 transition: background-color 0.3s ease, box-shadow 0.3s ease;
100 }
101 </style>
102</head>
103<body>
104 <div class="container">
105 <h1>JavaScript Console</h1>
106 <button id="executeBtn">Execute</button>
107 <div id="output"></div>
108 </div>
109
110 <script>
111 // Override console.log to display messages in the #output element
112 (function () {
113 // Override console.log
114 const originalLog = console.log;
115 console.log = function (...args) {
116 originalLog.apply(console, args);
117 const message = document.createElement('div');
118 message.textContent = args
119 .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
120 .join(" ");
121 output.appendChild(message);
122 };
123
124 // Override console.error
125 const originalError = console.error;
126 console.error = function (...args) {
127 originalError.apply(console, args);
128 const message = document.createElement('div');
129 message.textContent = args
130 .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
131 .join(" ");
132 message.style.color = 'red'; // Color error messages red
133 output.appendChild(message);
134 };
135 })();
136
137 document.getElementById('executeBtn').addEventListener('click', () => {
138 // Prevent multiple loads
139 if (document.getElementById('externalScript')) return;
140
141 const script = document.createElement('script');
142 script.src = 'javascript-indexed-db.js';
143 script.id = 'externalScript';
144 //script.onload = () => console.log('javascript-indexed-db.js loaded and executed.');
145 //script.onerror = () => console.log('Failed to load javascript-indexed-db.js.');
146 document.body.appendChild(script);
147 });
148 </script>
149</body>
150</html>
JavaScript dan IndexedDB
IndexedDB
adalah basis data penyimpanan key-value asinkron yang terintegrasi dalam browser. Itu menawarkan fitur yang mirip dengan basis data relasional dan memungkinkan Anda untuk menyimpan serta mencari sejumlah besar data terstruktur di sisi klien. Ini sangat berguna untuk aplikasi yang mendukung mode offline dan PWA (Progressive Web Apps).
Fitur-fitur dari IndexedDB
- Beroperasi dengan cara asinkron dan berbasis event.
- Objek JavaScript dapat disimpan di dalam object store.
- Pencarian dengan query atau indeks dimungkinkan.
- Ini memiliki kapasitas penyimpanan yang besar (ratusan MB atau lebih), memungkinkan Anda menyimpan lebih banyak data dibandingkan dengan cookies atau localStorage.
Membuka dan Membuat Database
Untuk menggunakan IndexedDB
, Anda harus membuka database terlebih dahulu. Jika database belum ada, maka akan dibuat secara otomatis.
1const request = indexedDB.open('MyDatabase', 1); // Specify DB name and version
- Metode
open
membuka database secara asinkron dan memicu tiga event berikut.
onsuccess
1// Fired when database is successfully opened
2request.onsuccess = (event) => {
3 const db = event.target.result; // Database instance
4 console.log('Database opened successfully:', db.name);
5};
- Event
onsuccess
dipicu ketika database berhasil dibuka. Operasi selanjutnya harus dilakukan menggunakanrequest.result
, yang akan tersedia pada titik ini.
onerror
1// Fired when database fails to open
2request.onerror = (event) => {
3 console.error('Failed to open database:', event.target.error);
4};
- Event
onerror
dipicu ketika database gagal dibuka. Pencatatan log dan penanganan error harus dilakukan di sini.
onupgradeneeded
1// Fired when database is newly created or upgraded
2request.onupgradeneeded = (event) => {
3 const db = event.target.result;
4 console.log('Database upgrade needed (or newly created):', db.name);
5
6 // Example: Create an object store (like a table) if it doesn’t exist
7 if (!db.objectStoreNames.contains('users')) {
8 db.createObjectStore('users', { keyPath: 'id' });
9 console.log('Object store "users" created');
10 }
11};
onupgradeneeded
dipicu ketika database baru dibuat atau ketika versi yang ditentukan lebih tinggi dari versi saat ini. Membuat tabel (object store) dan mendefinisikan skema harus dilakukan pada saat ini.
Membuat Object Store
Pertama, buatlah 'object store' (setara dengan tabel) di dalam onupgradeneeded
.
1request.onupgradeneeded = function (event) {
2 const db = event.target.result;
3 console.log("onupgradeneeded triggered. Database version:", db.version);
4 if (!db.objectStoreNames.contains('users')) {
5 console.log("Creating object store: users");
6 const store = db.createObjectStore('users', { keyPath: 'id' });
7 store.createIndex('name', 'name', { unique: false });
8 }
9};
Di sini, pengaturan berikut diterapkan:.
-
createObjectStore
createObjectStore
adalah metode untuk membuat object store baru di dalam database. Anda dapat menyimpan record dalam object store dan menentukan cara pengelolaan data dengan menetapkankeyPath
serta opsi lainnya. Proses ini harus dilakukan di dalam eventonupgradeneeded
. -
keyPath: 'id'
AturkeyPath
ke propertiid
, yang berarti setiap record diidentifikasi secara unik sebagai primary key. Hal ini memungkinkanid
digunakan secara otomatis saat menambah, mencari, atau memperbarui data. -
createIndex
Gunakan metodecreateIndex
untuk membuat indeks berdasarkan propertiname
untuk pencarian. Dengan mengaturunique: false
, beberapa record denganname
yang sama diperbolehkan.
Menangani Koneksi Database yang Berhasil
Tetapkan proses yang akan dijalankan setelah koneksi database berhasil ke event onsuccess
. Dalam proses ini, Anda mendapatkan instance database dan bersiap untuk membaca dan menulis data.
1request.onsuccess = function (event) {
2 const db = event.target.result;
3 console.log('Database opened successfully');
4 // Use db for reading and writing in subsequent operations
5};
-
Proses ini dijalankan ketika koneksi ke database
IndexedDB
berhasil. -
event.target.result
berisi instance database yang telah dibuka (objekIDBDatabase
), yang digunakan untuk memulai transaksi dan mengakses object store. -
Operasi baca-tulis seperti menambah, mengambil, memperbarui, dan menghapus data dilakukan menggunakan objek
db
.
Pada titik ini, database sudah siap sehingga Anda dapat mulai transaksi dengan aman.
Menambah Data
Berikut cara menambah data baru ke IndexedDB
.
1function addUser(db, user) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.add(user);
5
6 request.onsuccess = () => {
7 console.log('User added:', user);
8 };
9
10 request.onerror = () => {
11 console.error('Add failed:', request.error);
12 };
13}
14
15// Example: Add a user
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 addUser(db, { id: 1, name: 'Alice' });
19 addUser(db, { id: 2, name: 'Bob' });
20 addUser(db, { id: 3, name: 'John' });
21};
- Buat transaksi dengan
db.transaction()
dan tentukan object store yang akan digunakan serta mode-nya (dalam kasus ini,readwrite
). - Tambahkan data baru dengan menggunakan metode
store.add()
. - Transaksi dikomit otomatis, tetapi jika Anda ingin mengelompokkan beberapa operasi, Anda dapat mengelolanya menggunakan event akhir transaksi.
Mengambil Data (Pencarian Berdasarkan Primary Key)
Berikut cara mengambil data spesifik menggunakan primary key.
1function getUserById(db, id) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.get(id);
5
6 request.onsuccess = () => {
7 if (request.result) {
8 console.log('User found:', request.result);
9 } else {
10 console.log('User not found');
11 }
12 };
13
14 request.onerror = () => {
15 console.error('Error retrieving user:', request.error);
16 };
17}
18
19// Example: Get a user by id
20request.onsuccess = function (event) {
21 const db = event.target.result;
22 getUserById(db, 1);
23};
- Buat transaksi read-only dengan
db.transaction()
. - Gunakan
store.get(id)
untuk mengambil data yang sesuai dengan primary key yang ditentukan. onsuccess
dipanggil saat pengambilan data berhasil, dan hasilnya akan ditampilkan jika tersedia. Jika tidak ada hasil, dianggap sebagai 'data yang sesuai tidak ditemukan.'.
Menggunakan Pencarian Indeks
Jika Anda ingin mencari berdasarkan properti selain primary key, gunakan indeks yang telah Anda buat sebelumnya.
1function getUserByName(db, name) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const index = store.index('name');
5 const request = index.get(name);
6
7 request.onsuccess = () => {
8 if (request.result) {
9 console.log('User by name:', request.result);
10 } else {
11 console.log('No user found with that name');
12 }
13 };
14
15 request.onerror = () => {
16 console.error('Error retrieving user by name:', request.error);
17 };
18}
19
20// Example: Get a user by id
21request.onsuccess = function (event) {
22 const db = event.target.result;
23 getUserByName(db, 'Alice');
24};
- Akses indeks
name
yang sudah dibuat sebelumnya menggunakanstore.index('name')
. index.get(value)
mengambil record pertama dengan nilai yang cocok. Jika beberapa record memiliki nilai yang sama, Anda dapat mengambil semuanya denganindex.getAll(value)
.
Memperbarui Data
Untuk memperbarui data yang sudah ada (overwrite), gunakan metode put()
.
Jika record dengan primary key yang cocok ada, maka akan diperbarui; jika tidak, record baru akan ditambahkan.
1function updateUser(db, updatedUser) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.put(updatedUser);
5
6 request.onsuccess = () => {
7 console.log('User updated:', updatedUser);
8 };
9
10 request.onerror = () => {
11 console.error('Update failed:', request.error);
12 };
13}
14
15// Example: Update user
16request.onsuccess = async (event) => {
17 const db = event.target.result;
18
19 // Test : update existing user
20 updateUser(db, { id: 3, name: 'John Updated' });
21
22 // Test : insert new user
23 updateUser(db, { id: 4, name: 'Charlie' });
24};
put()
adalah metode yang praktis yang mendukung pembaruan sekaligus penambahan.- Jika data dengan primary key yang sama sudah ada, data tersebut akan ditimpa.
- Jika Anda ingin memeriksa keberadaan data sebelum memperbarui, Anda dapat menggunakan
get()
untuk konfirmasi terlebih dahulu.
Menghapus Data
Untuk menghapus data yang sesuai dengan primary key tertentu, gunakan metode delete()
.
1function deleteUser(db, id) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.delete(id);
5
6 request.onsuccess = () => {
7 console.log(`User with id=${id} deleted successfully`);
8 };
9
10 request.onerror = () => {
11 console.error(`Failed to delete user with id=${id}:`, request.error);
12 };
13}
14
15// Example: Delete a user by id
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 deleteUser(db, 4);
19};
- Gunakan
store.delete(id)
untuk menghapus data dengan primary key yang cocok. - Perlu diketahui, meskipun data tidak ada, tidak akan terjadi error dan dianggap sebagai keberhasilan.
- Menerapkan penanganan error akan membuat kode Anda lebih kuat.
Mengambil Semua Data
getAll()
Untuk mengambil semua record dalam object store, gunakan metode getAll()
.
1function getAllUsers(db) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.getAll();
5
6 request.onsuccess = () => {
7 console.log('All users:', request.result);
8 };
9
10 request.onerror = () => {
11 console.error('Failed to retrieve users:', request.error);
12 };
13}
14
15// Example: Get all users
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 getAllUsers(db);
19};
getAll()
mengambil semua record dalam object store yang ditentukan sebagai sebuah array.- Bahkan jika mengambil sejumlah besar data sekaligus, prosesnya tetap efisien.
- Hasilnya disimpan sebagai array di
request.result
. - Selalu tambahkan penanganan error agar Anda dapat mengatasi kegagalan.
openCursor()
openCursor()
adalah metode untuk menjelajah record di object store atau indeks secara berurutan. Ini berguna jika Anda ingin memproses data satu per satu daripada mengambil semuanya sekaligus.
1function getAllUsersWithCursor(db) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.openCursor();
5
6 request.onsuccess = () => {
7 const cursor = request.result;
8 if (cursor) {
9 console.log('User:', cursor.value); // Process the current record
10 cursor.continue(); // Move to the next record
11 } else {
12 console.log('All users have been processed.');
13 }
14 };
15
16 request.onerror = () => {
17 console.error('Failed to open cursor:', request.error);
18 };
19}
20
21// Example: Get all users
22request.onsuccess = function (event) {
23 const db = event.target.result;
24 getAllUsersWithCursor(db);
25};
- Dengan menggunakan
openCursor()
, Anda memulai kursor dan mengambil semua record di object store satu per satu. - Gunakan
cursor.value
untuk mendapatkan objek data dari record yang sedang diakses. - Pindahkan kursor ke record berikutnya dengan
cursor.continue()
. - Ketika
cursor === null
, semua record telah dijelajahi.
Contoh proses pembaruan menggunakan openCursor()
Sebagai contoh, proses mengubah nama pengguna yang name
-nya adalah Alice
menjadi Alicia
akan seperti berikut:.
1function updateUserName(db, oldName, newName) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const index = store.index('name'); // Use the 'name' index
5 const request = index.openCursor(IDBKeyRange.only(oldName));
6
7 request.onsuccess = () => {
8 const cursor = request.result;
9 if (cursor) {
10 const user = cursor.value;
11 user.name = newName; // Update the name
12 const updateRequest = cursor.update(user);
13
14 updateRequest.onsuccess = () => {
15 console.log('Updated user:', user);
16 };
17
18 updateRequest.onerror = () => {
19 console.error('Failed to update user:', updateRequest.error);
20 };
21
22 cursor.continue();
23 } else {
24 console.log('All matching users have been updated.');
25 }
26 };
27
28 request.onerror = () => {
29 console.error('Cursor error:', request.error);
30 };
31}
32
33// Example: Update user name
34request.onsuccess = function (event) {
35 const db = event.target.result;
36 updateUserName(db, 'Alice', 'Alicia');
37};
-
IDBKeyRange.only(oldName)
Dengan menggunakanIDBKeyRange.only
, Anda dapat menargetkan hanya data yang kuncinya persis sama denganoldName
. Ini berguna ketika Anda ingin mengakses nilai tertentu secara langsung. -
cursor.update()
Setelah memperbaruicursor.value
, memanggilupdate()
akan menimpa data yang bersangkutan. -
Menangani Kecocokan Ganda Dengan memanggil
cursor.continue()
, Anda dapat memindahkan kursor ke data berikutnya yang cocok. Ini memungkinkan Anda memproses beberapa data yang cocok dengan kunci atau kondisi yang sama secara berurutan. -
Penanganan Kesalahan Dengan mencetak log pada
onerror
ketika sebuah proses gagal, akan lebih mudah untuk menyelidiki penyebab serta memperbaiki masalah saat operasional.
Contoh proses penghapusan menggunakan openCursor()
Sebagai contoh, proses menghapus semua pengguna yang name
-nya adalah Bob
akan seperti berikut:.
1function deleteUsersByName(db, targetName) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const index = store.index('name');
5 const request = index.openCursor(IDBKeyRange.only(targetName));
6
7 request.onsuccess = () => {
8 const cursor = request.result;
9 if (cursor) {
10 const deleteRequest = cursor.delete();
11
12 deleteRequest.onsuccess = () => {
13 console.log('Deleted user:', cursor.value);
14 };
15
16 deleteRequest.onerror = () => {
17 console.error('Failed to delete user:', deleteRequest.error);
18 };
19
20 cursor.continue();
21 } else {
22 console.log('All matching users have been deleted.');
23 }
24 };
25
26 request.onerror = () => {
27 console.error('Cursor error:', request.error);
28 };
29}
30
31// Example: Delete user by name
32request.onsuccess = function (event) {
33 const db = event.target.result;
34 deleteUsersByName(db, 'Bob');
35};
cursor.delete()
Dengancursor.delete()
, data pada posisi kursor saat ini akan dihapus. Karena hasilnya dikembalikan secara asinkron, Anda dapat memeriksa prosesnya dionsuccess
.
Catatan tentang Transaksi dan Pemrosesan Asinkron
IndexedDB
bersifat asinkron dan berbasis event. Semua operasi harus ditangani dengan event onsuccess
atau onerror
. Ketika Anda ingin mengelompokkan beberapa proses sekaligus, akan lebih mudah jika membungkusnya dalam sebuah Promise
.
1function openDatabase() {
2 return new Promise((resolve, reject) => {
3 const request = indexedDB.open('MyDatabase', 1);
4
5 request.onupgradeneeded = function (event) {
6 const db = event.target.result;
7
8 // Initialization process (creating object stores and indexes, etc.)
9 const store = db.createObjectStore('users', { keyPath: 'id' });
10 store.createIndex('name', 'name', { unique: false });
11 };
12
13 request.onsuccess = function (event) {
14 const db = event.target.result;
15 resolve(db);
16 };
17
18 request.onerror = function () {
19 reject(request.error);
20 };
21 });
22}
23
24function addUserAsync(db, user) {
25 return new Promise((resolve, reject) => {
26 const transaction = db.transaction('users', 'readwrite');
27 const store = transaction.objectStore('users');
28 const request = store.add(user);
29
30 request.onsuccess = () => resolve();
31 request.onerror = () => reject(request.error);
32 });
33}
34
35async function main() {
36 try {
37 const db = await openDatabase();
38 await addUserAsync(db, { id: 1, name: 'Alice' });
39 console.log('User added successfully');
40 } catch (error) {
41 console.error('Error:', error);
42 }
43}
- Membungkus fungsi seperti
openDatabase
atauaddUserAsync
denganPromise
memungkinkan Anda menangani proses asinkron dengan lebih intuitif menggunakanasync/await
. - Ini menghindari callback hell dan membuat kode lebih mudah dibaca.
Ringkasan
IndexedDB
adalah fitur yang sangat kuat jika Anda ingin melakukan manajemen data yang lebih canggih di sisi browser. Pada awalnya, Anda mungkin akan bingung dengan pemrosesan asinkron berbasis event, tetapi setelah memahami strukturnya, Anda dapat melakukan operasi data secara penuh di sisi klien.
Khususnya, memperhatikan poin-poin berikut akan membantu Anda dalam menggunakannya dengan lebih lancar:.
- Lakukan pengaturan awal dengan
onupgradeneeded
. - Perhatikan mode baca/tulis dari transaksi.
- Indeks memungkinkan pencarian yang efisien.
- Data dapat disimpan sebagai objek, sehingga sangat kompatibel dengan JSON.
Dengan menguasai IndexedDB
, pengelolaan data untuk PWA dan aplikasi offline menjadi jauh lebih mudah.
Anda dapat mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Silakan periksa juga saluran YouTube kami.