Created
February 25, 2021 15:23
-
-
Save prof3ssorSt3v3/4f98bf8fdf62a02d09d5ad822feddeed to your computer and use it in GitHub Desktop.
IndexedDB Part 7 - Using Indexes and KeyRanges
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { uid } from './uid.js'; | |
import { state } from './data.js'; | |
//https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase | |
const IDB = (function init() { | |
let db = null; | |
let objectStore = null; | |
let DBOpenReq = indexedDB.open('WhiskeyDB', 4); | |
DBOpenReq.addEventListener('error', (err) => { | |
//Error occurred while trying to open DB | |
console.warn(err); | |
}); | |
DBOpenReq.addEventListener('success', (ev) => { | |
//DB has been opened... after upgradeneeded | |
db = ev.target.result; | |
console.log('success opening DB'); | |
if (typeof state !== 'undefined') { | |
let tx = makeTX('whiskeyStore', 'readwrite'); | |
tx.oncomplete = (ev) => { | |
console.log('finished adding the data'); | |
buildList(); | |
}; | |
let store = tx.objectStore('whiskeyStore'); | |
let request = store.getAll(); | |
// let request = store.delete .deleteIndex .clear() | |
request.onsuccess = (ev) => { | |
if (ev.target.result.length === 0) { | |
//OR ev.target.result.length !== state.length | |
state.forEach((obj) => { | |
let req = store.add(obj); | |
req.onsuccess = (ev) => { | |
console.log('added an object'); | |
}; | |
// tx.abort() - if you want to kill your transaction | |
req.onerror = (err) => { | |
console.warn(err); | |
}; | |
}); | |
} | |
}; | |
} else { | |
buildList(); | |
} | |
}); | |
DBOpenReq.addEventListener('upgradeneeded', (ev) => { | |
//first time opening this DB | |
//OR a new version was passed into open() | |
db = ev.target.result; | |
let oldVersion = ev.oldVersion; | |
let newVersion = ev.newVersion || db.version; | |
console.log('DB updated from version', oldVersion, 'to', newVersion); | |
// console.log('upgrade', db); | |
if (db.objectStoreNames.contains('whiskeyStore')) { | |
db.deleteObjectStore('whiskeyStore'); | |
} | |
//create the ObjectStore | |
objectStore = db.createObjectStore('whiskeyStore', { | |
keyPath: 'id', | |
}); | |
//add the indexes | |
objectStore.createIndex('nameIDX', 'name', { unique: false }); | |
objectStore.createIndex('countryIDX', 'country', { unique: false }); | |
objectStore.createIndex('ageIDX', 'age', { unique: false }); | |
objectStore.createIndex('editIDX', 'lastEdit', { unique: false }); | |
}); | |
document.getElementById('btnUpdate').addEventListener('click', (ev) => { | |
ev.preventDefault(); | |
let name = document.getElementById('name').value.trim(); | |
let country = document.getElementById('country').value.trim(); | |
let age = parseInt(document.getElementById('age').value); | |
let owned = document.getElementById('isOwned').checked; | |
//id | |
let key = document.whiskeyForm.getAttribute('data-key'); | |
if (key) { | |
let whiskey = { | |
id: key, | |
name, | |
country, | |
age, | |
owned, | |
lastEdit: Date.now(), | |
}; | |
let tx = makeTX('whiskeyStore', 'readwrite'); | |
tx.oncomplete = (ev) => { | |
console.log(ev); | |
buildList(); | |
clearForm(); | |
}; | |
let store = tx.objectStore('whiskeyStore'); | |
let request = store.put(whiskey); //request a put/update | |
request.onsuccess = (ev) => { | |
console.log('successfully updated an object'); | |
//move on to the next request in the transaction or | |
//commit the transaction | |
}; | |
request.onerror = (err) => { | |
console.log('error in request to update'); | |
}; | |
} | |
}); | |
document.getElementById('btnDelete').addEventListener('click', (ev) => { | |
ev.preventDefault(); | |
//id | |
let key = document.whiskeyForm.getAttribute('data-key'); | |
if (key) { | |
let tx = makeTX('whiskeyStore', 'readwrite'); | |
tx.oncomplete = (ev) => { | |
console.log(ev); | |
buildList(); | |
clearForm(); | |
}; | |
let store = tx.objectStore('whiskeyStore'); | |
let request = store.delete(key); //request a delete | |
request.onsuccess = (ev) => { | |
console.log('successfully deleted an object'); | |
//move on to the next request in the transaction or | |
//commit the transaction | |
}; | |
request.onerror = (err) => { | |
console.log('error in request to delete'); | |
}; | |
} | |
}); | |
document.getElementById('btnAdd').addEventListener('click', (ev) => { | |
ev.preventDefault(); | |
//one of the form buttons was clicked | |
let name = document.getElementById('name').value.trim(); | |
let country = document.getElementById('country').value.trim(); | |
let age = parseInt(document.getElementById('age').value); | |
let owned = document.getElementById('isOwned').checked; | |
let whiskey = { | |
id: uid(), | |
name, | |
country, | |
age, | |
owned, | |
lastEdit: Date.now(), | |
}; | |
let tx = makeTX('whiskeyStore', 'readwrite'); | |
tx.oncomplete = (ev) => { | |
//console.log(ev); | |
buildList(); | |
clearForm(); | |
}; | |
let store = tx.objectStore('whiskeyStore'); | |
let request = store.add(whiskey); //request an insert/add | |
request.onsuccess = (ev) => { | |
console.log('successfully added an object'); | |
//move on to the next request in the transaction or | |
//commit the transaction | |
}; | |
request.onerror = (err) => { | |
console.log('error in request to add'); | |
}; | |
}); | |
function buildList() { | |
//use getAll to get an array of objects from our store | |
let list = document.querySelector('.wList'); | |
list.innerHTML = `<li>Loading...</li>`; | |
let tx = makeTX('whiskeyStore', 'readonly'); | |
tx.oncomplete = (ev) => { | |
//transaction for reading all objects is complete | |
}; | |
let store = tx.objectStore('whiskeyStore'); | |
//let getReq = store.getAll(); | |
// let range = IDBKeyRange.lowerBound(14, true); //false 14 or higher... true 15 or higher | |
let range = IDBKeyRange.bound(1, 10, false, false); | |
let idx = store.index('ageIDX'); | |
let getReq = idx.getAll(range); | |
//returns an array | |
//option can pass in a key or a keyRange | |
getReq.onsuccess = (ev) => { | |
//getAll was successful | |
let request = ev.target; //request === getReq === ev.target | |
//console.log({ request }); | |
list.innerHTML = request.result | |
.map((whiskey) => { | |
return `<li data-key="${whiskey.id}"><span>${whiskey.name}</span> ${whiskey.age}</li>`; | |
}) | |
.join('\n'); | |
}; | |
getReq.onerror = (err) => { | |
console.warn(err); | |
}; | |
} | |
document.querySelector('.wList').addEventListener('click', (ev) => { | |
let li = ev.target.closest('[data-key]'); | |
let id = li.getAttribute('data-key'); | |
console.log(li, id); | |
let tx = makeTX('whiskeyStore', 'readonly'); | |
tx.oncomplete = (ev) => { | |
//get transaction complete | |
}; | |
let store = tx.objectStore('whiskeyStore'); | |
let req = store.get(id); | |
req.onsuccess = (ev) => { | |
let request = ev.target; | |
let whiskey = request.result; | |
document.getElementById('name').value = whiskey.name; | |
document.getElementById('country').value = whiskey.country; | |
document.getElementById('age').value = whiskey.age; | |
document.getElementById('isOwned').checked = whiskey.owned; | |
//put the whiskey id into a form attribute | |
document.whiskeyForm.setAttribute('data-key', whiskey.id); | |
}; | |
req.onerror = (err) => { | |
console.warn(err); | |
}; | |
}); | |
function makeTX(storeName, mode) { | |
let tx = db.transaction(storeName, mode); | |
tx.onerror = (err) => { | |
console.warn(err); | |
}; | |
return tx; | |
} | |
document.getElementById('btnClear').addEventListener('click', clearForm); | |
function clearForm(ev) { | |
if (ev) ev.preventDefault(); | |
document.whiskeyForm.reset(); | |
document.whiskeyForm.removeAttribute('data-key'); | |
} | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const state = [ | |
{ | |
age: 8, | |
country: 'Scotland', | |
id: 'KLIE87ES-023IPDDPYG', | |
name: 'Lagavulin', | |
owned: true, | |
lastEdit: 1613990943287, | |
}, | |
{ | |
age: 3, | |
country: 'Canada', | |
id: 'KLIE1ST7-00IM8EM2Y6V7', | |
name: 'Crown Royal', | |
owned: true, | |
lastEdit: 1613897343287, | |
}, | |
{ | |
age: 16, | |
country: 'Scotland', | |
id: 'KLH80H0H-00TNHA9Z71KD', | |
name: 'Lagavulin', | |
owned: true, | |
lastEdit: 1614170943287, | |
}, | |
{ | |
age: 14, | |
country: 'Scotland', | |
id: 'KLH7ZQBT-026IOWX9I4N3', | |
name: 'Oban', | |
owned: true, | |
lastEdit: 1614062943287, | |
}, | |
]; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Simple IndexedDB</title> | |
<link rel="stylesheet" href="main.css" /> | |
</head> | |
<body> | |
<header> | |
<h1>IndexedDB</h1> | |
<h2>Using Vanilla JS</h2> | |
</header> | |
<main> | |
<p>This example is using plain vanilla JS and core indexedDB methods.</p> | |
<p> | |
If you are familiar with document databases like MongoDB, you can think | |
of an ObjectStore as a collection. | |
</p> | |
<p>The Core Object and Interfaces are:</p> | |
<ul> | |
<li> | |
<code>window.indexedDB</code> The database object holds Object Stores. | |
</li> | |
<li> | |
<code>IDBObjectStore</code> An Object Store inside a database. (A | |
Collection of objects) | |
</li> | |
<li> | |
<code>IDBRequest</code> A request object for add, put, delete, etc. | |
</li> | |
<li><code>IDBTransaction</code> A wrapper around requests.</li> | |
<li><code>IDBIndex</code> An index added to an Object Store</li> | |
<li> | |
<code>IDBCursor</code> To track current position inside the results of | |
a request. | |
</li> | |
<li><code>IDBKeyRange</code> A range of values for matching a key.</li> | |
</ul> | |
<form name="whiskeyForm"> | |
<fieldset> | |
<p> | |
<label for="name">Whiskey: </label> | |
<input type="text" id="name" placeholder="Whiskey Name" required /> | |
</p> | |
<p> | |
<label for="country">Country of Origin: </label> | |
<input type="text" id="country" value="" required /> | |
</p> | |
<p> | |
<label for="age">Years Old: </label> | |
<input | |
type="text" | |
inputmode="numeric" | |
pattern="[\d]+" | |
id="age" | |
required | |
/> | |
</p> | |
<p> | |
<label for="isOwned">Owned: </label> | |
<input type="checkbox" id="isOwned" value="yes" /> | |
</p> | |
<p> | |
<button id="btnAdd">Add Whiskey</button> | |
<button id="btnUpdate">Update Whiskey</button> | |
<button id="btnDelete">Delete Whiskey</button> | |
<button id="btnClear">Reset Form</button> | |
</p> | |
</fieldset> | |
</form> | |
<section> | |
<h3>List of Whiskeys</h3> | |
<ul class="wList"> | |
<!-- build list here --> | |
</ul> | |
</section> | |
</main> | |
<footer> | |
<p>© 2021 Chicken Stuff Inc.</p> | |
</footer> | |
<script type="module" src="app.js"></script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* { | |
padding: 0; | |
margin: 0; | |
box-sizing: border-box; | |
} | |
html { | |
font-size: 16px; | |
font-weight: 300; | |
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, | |
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; | |
line-height: 1.5; | |
color: #eee; | |
background-color: #333; | |
} | |
body { | |
background-color: #333; | |
min-height: 100vh; | |
padding-bottom: 4rem; | |
} | |
header, | |
main, | |
footer { | |
padding: 1rem 2rem; | |
} | |
h1 { | |
color: orange; | |
font-size: 3.6rem; | |
font-weight: 700; | |
} | |
h2 { | |
color: orangered; | |
font-size: 2.4rem; | |
font-weight: 700; | |
} | |
p { | |
font-size: 1.2rem; | |
font-weight: 300; | |
margin: 1rem 0; | |
} | |
li { | |
list-style-position: inside; | |
margin-left: 2rem; | |
font-size: 1.2rem; | |
font-weight: 500; | |
} | |
form { | |
padding: 1rem 1rem; | |
} | |
fieldset { | |
padding: 1rem; | |
} | |
form p { | |
display: flex; | |
flex-direction: column; | |
justify-content: flex-start; | |
align-items: flex-start; | |
margin: 1rem 0; | |
} | |
label { | |
font-size: 1rem; | |
} | |
input[type='text'] { | |
padding: 0.2rem 1rem; | |
font-size: 1rem; | |
line-height: 1.5; | |
width: 30ch; | |
} | |
input[type='checkbox'] { | |
font-size: 1rem; | |
line-height: 1.5; | |
height: 1.5rem; | |
width: 1.5rem; | |
} | |
button { | |
padding: 0.2rem 2rem; | |
margin: 1rem 0; | |
font-size: 1.2rem; | |
border: none; | |
color: white; | |
background-color: cornflowerblue; | |
} | |
.highlighted { | |
color: #333; | |
background-color: gold; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const uid = () => { | |
let timmy = Date.now().toString(36).toLocaleUpperCase(); | |
let randy = parseInt(Math.random() * Number.MAX_SAFE_INTEGER); | |
randy = randy.toString(36).slice(0, 12).padStart(12, '0').toLocaleUpperCase(); | |
return ''.concat(timmy, '-', randy); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment