JavaScript at IndexedDB

JavaScript at IndexedDB

Sa artikulong ito, ipapaliwanag namin ang JavaScript at IndexedDB.

Ang tutorial na ito ay nagbibigay ng sunud-sunod na paliwanag tungkol sa JavaScript at IndexedDB, kabilang ang praktikal na sample code bawat hakbang upang mapalalim ang iyong pag-unawa.

YouTube Video

javascript-indexed-db.html
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4  <meta charset="UTF-8">
  5  <title>JavaScript &amp; 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 at IndexedDB

Ang IndexedDB ay isang asynchronous na key-value store database na naka-built-in sa mga browser. Nag-aalok ito ng mga tampok na katulad ng relational databases at nagbibigay-daan sa iyo na mag-imbak at maghanap ng malaking halaga ng structured data sa client side. Ito ay lalo nang kapaki-pakinabang para sa mga application na kayang gumana offline at sa mga PWA (Progressive Web Apps).

Mga katangian ng IndexedDB

  • Gumagana sa isang asynchronous at event-driven na paraan.
  • Maaaring mag-imbak ng JavaScript objects sa object stores.
  • Maaaring maghanap gamit ang queries o indexes.
  • Mayroon itong malaking storage capacity (daang MB o higit pa), na nagbibigay-daan sa iyong mag-imbak ng mas maraming data kaysa cookies o localStorage.

Pagbubukas at Paglikha ng Database

Upang magamit ang IndexedDB, kailangan mo munang buksan ang isang database. Kung hindi ito umiiral, ito ay awtomatikong nililikha.

1const request = indexedDB.open('MyDatabase', 1); // Specify DB name and version
  • Ang open method ay nagbubukas ng database nang asynchronous at nagti-trigger ng sumusunod na tatlong events.

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};
  • Ang onsuccess event ay nai-trigger kapag matagumpay na nabuksan ang database. Ang mga susunod na operasyon ay dapat gawin gamit ang request.result, na magagamit na sa puntong ito.

onerror

1// Fired when database fails to open
2request.onerror = (event) => {
3    console.error('Failed to open database:', event.target.error);
4};
  • Ang onerror event ay nai-trigger kapag nabigo ang pagbukas ng database. Dito dapat gawin ang error logging at error handling.

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};
  • Ang onupgradeneeded ay nai-trigger kapag bagong likha ang database o kapag ang tinukoy na bersyon ay mas mataas kaysa sa kasalukuyang bersyon. Ang paglikha ng mga table (object stores) at pagde-define ng schema ay dapat gawin sa time na ito.

Paglikha ng Object Store

Una, lumikha ng 'object store' (katumbas ng table) sa loob ng 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};

Dito, ang sumusunod na mga setting ay inilalagay:.

  • createObjectStore Ang createObjectStore ay isang method para lumikha ng bagong object store sa database. Maaari kang mag-imbak ng records sa isang object store at matukoy kung paano i-ma-manage ang data sa pamamagitan ng pag-specify ng keyPath at iba pang mga opsyon. Ang prosesong ito ay dapat gawin sa loob ng onupgradeneeded event.

  • keyPath: 'id' Itakda ang keyPath sa id property, na natatanging tumutukoy sa bawat record bilang primary key. Sa ganitong paraan, ang id ay awtomatikong magagamit kapag nagdadagdag, naghahanap, o nag-a-update ng data.

  • createIndex Gamitin ang createIndex method para lumikha ng index batay sa name property para sa pag-search. Sa pamamagitan ng pag-set ng unique: false, maaari kang magkaroon ng maraming records na may parehong name.

Paghawak ng Matagumpay na Database Connection

Itakda ang mga prosesong isasagawa kapag matagumpay ang database connection sa onsuccess event. Sa prosesong ito, kinukuha mo ang database instance at naghahanda para sa pagbabasa at pagsulat ng 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};
  • Ang prosesong ito ay tumatakbo kapag matagumpay na nakakonekta ang IndexedDB database.

  • Ang event.target.result ay naglalaman ng binuksang database instance (IDBDatabase object), na ginagamit upang magsimula ng transactions at mag-access ng object stores.

  • Ang aktwal na pagbabasa at pagsulat tulad ng pagdagdag, pagkuha, pag-update, at pagbura ng data ay ginagawa gamit ang db object.

Sa puntong ito, handa na ang database kaya maaari ka nang ligtas na magsimula ng mga transaction.

Pagdagdag ng Data

Ganito magdagdag ng bagong data sa 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};
  • Lumikha ng transaction gamit ang db.transaction() at tukuyin ang object store na gagamitin at ang mode (sa kasong ito, readwrite).
  • Magdagdag ng bagong data gamit ang store.add() method.
  • Ang mga transaction ay awtomatikong nakakamit, ngunit kung nais mong i-grupo ang maraming operasyon, maaari mo itong i-manage gamit ang transaction end events.

Pagkuha ng Data (Primary Key Search)

Narito kung paano kunin ang partikular na data gamit ang 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};
  • Lumikha ng read-only na transaction gamit ang db.transaction().
  • Gamitin ang store.get(id) upang kunin ang data na tumutugma sa tinukoy na primary key.
  • Ang onsuccess ay tinatawag kapag tagumpay ang pagkuha at ipinapakita ang resulta kung mayroon. Kung walang resulta, ito ay itinatrato bilang 'walang tumutugmang data.'.

Paggamit ng Index Search

Kung nais mong maghanap gamit ang property bukod sa primary key, gamitin ang index na nilikha mo na dati.

 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};
  • Access-in ang naunang nilikhang name index gamit ang store.index('name').
  • Ang index.get(value) ay kumukuha ng unang record na tumutugma sa value. Kung maraming records ang may parehong value, maaari mong kunin lahat gamit ang index.getAll(value).

Pag-update ng Data

Upang mag-update ng umiiral na data (i-overwrite), gamitin ang put() method.

Kung may record na tumutugma sa primary key, ito ay na-update; kung wala, nadaragdagan ng bagong record.

 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};
  • Ang put() ay isang maginhawang method na sumusuporta sa pag-update at pagdagdag.
  • Kung may data na may parehong primary key, ito ay mae-overwrite.
  • Kung nais mong tiyakin kung umiiral na bago mag-update, puwede kang gumamit ng get() para i-confirm muna.

Pagbubura ng Data

Para tanggalin ang data na tumutugma sa tinukoy na primary key, gamitin ang delete() method.

 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};
  • Gamitin ang store.delete(id) para magbura ng data na may parehong primary key.
  • Tandaan na kahit walang umiiral na data, walang error na mangyayari at ito ay ituturing na tagumpay.
  • Ang paglalagay ng error handling ay magdudulot ng mas matibay na code.

Pagkuha ng Lahat ng Data

getAll()

Upang makuha lahat ng record sa object store, gamitin ang getAll() method.

 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};
  • Ang getAll() ay kumukuha ng lahat ng record sa tinukoy na object store bilang array.
  • Kahit kumuha ng malalaking data ng sabay-sabay, ito ay napoproseso nang mahusay.
  • Ang mga resulta ay nakaimbak bilang array sa request.result.
  • Laging maglagay ng error handling para makasiguro sa mga aberya.

openCursor()

Ang openCursor() ay isang method para sa sunud-sunod na paglalakbay sa mga record ng object store o index. Makatutulong ito kapag nais mong iproseso ang data nang paisa-isang item imbes na sabay-sabay.

 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};
  • Sa paggamit ng openCursor(), magsisimulang gamitin ang cursor at makukuha ang lahat ng records sa object store isa-isa.
  • Gamitin ang cursor.value para makuha ang data object ng kasalukuyang record.
  • Ilipat ang cursor sa susunod na record gamit ang cursor.continue().
  • Kapag cursor === null, lahat ng records ay nadaanan na.

Halimbawa ng proseso ng pag-update gamit ang openCursor()

Halimbawa, ang proseso ng pagpapalit ng pangalan ng isang user na ang name ay Alice sa Alicia ay ganito ang magiging hitsura:.

 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) Sa pamamagitan ng paggamit ng IDBKeyRange.only, maaari mo lamang i-target ang mga record na eksaktong tumutugma ang key sa oldName. Kapaki-pakinabang ito kapag gusto mong direktang ma-access ang isang partikular na value.

  • cursor.update() Pagkatapos baguhin ang cursor.value, kapag tinawag ang update(), ang katumbas na record ay marereplasan.

  • Paghawak ng Maramihang Magkakatugma Sa pamamagitan ng pagtawag sa cursor.continue(), maaari mong ilipat ang cursor sa susunod na katugmang record. Pinapayagan ka nitong i-proseso ang maraming record na tumutugma sa parehong key o kondisyon nang sunud-sunod.

  • Paghawak ng Error Sa pamamagitan ng pag-log sa onerror kapag nabigo ang proseso, mas madali mong malalaman ang sanhi ng problema at magawa ang troubleshooting habang ginagamit.

Halimbawa ng proseso ng pagtanggal gamit ang openCursor()

Halimbawa, ang proseso ng pagtanggal ng lahat ng user na ang name ay Bob ay ganito ang magiging hitsura:.

 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() Sa pamamagitan ng cursor.delete(), natatanggal ang record na nasa kasalukuyang posisyon ng cursor. Dahil asynchronous bumabalik ang resulta, maaari mong tingnan ang proseso sa onsuccess.

Paalala hinggil sa Transaksyon at Hindi-Sabay na Pagpoproseso

Ang IndexedDB ay asynchronous at event-driven. Lahat ng operasyon ay dapat gamitan ng onsuccess o onerror events. Kapag gusto mong pagsama-samahin ang maraming proseso, mainam na balutin ito sa isang 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}
  • Ang paglalagay ng mga function tulad ng openDatabase o addUserAsync sa loob ng isang Promise ay nagbibigay-daan upang mas madaling pamahalaan ang asynchronous na proseso gamit ang async/await.
  • Iniiwasan nito ang callback hell at nagiging mas madaling basahin ang code.

Buod

Ang IndexedDB ay isang napakalakas na tampok kapag nais mong magsagawa ng advanced na pamamahala ng data sa browser. Sa simula, maaaring malito ka sa event-based asynchronous na proseso, pero kapag naintindihan mo na ang istraktura, maaari kang magsagawa ng kumpletong data operations sa client side.

Lalo na, ang pagtanda sa mga sumusunod na punto ay makakatulong para mas magamit mo ito nang maayos:.

  • Isagawa ang paunang setup gamit ang onupgradeneeded.
  • Bigyang-pansin ang mode ng pagbasa/pagsulat ng mga transaksyon.
  • Nagpapadali ang mga index ng mahusay na paghahanap.
  • Maaaring i-store ang data bilang mga object, kaya compatible ito sa JSON.

Kung mahusay mong gagamitin ang IndexedDB, mas mapapadali ang data management para sa mga PWA at offline na app.

Maaari mong sundan ang artikulo sa itaas gamit ang Visual Studio Code sa aming YouTube channel. Paki-check din ang aming YouTube channel.

YouTube Video