Spaces:
Running
Running
liewchooichin
commited on
Commit
•
8196597
1
Parent(s):
80d53d3
simple notes app
Browse files- simple-notes/index.html +55 -0
- simple-notes/index.js +185 -0
- simple-notes/index.ts +221 -0
- simple-notes/style.css +60 -0
- simple-notes/tsconfig.json +6 -0
simple-notes/index.html
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en-US">
|
3 |
+
<head>
|
4 |
+
<meta charset="utf-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>IndexedDB demo</title>
|
7 |
+
<link href="../favicon.svg" rel="icon">
|
8 |
+
<link href="./style.css" rel="stylesheet">
|
9 |
+
<script src="./index.js" defer></script>
|
10 |
+
</head>
|
11 |
+
<body>
|
12 |
+
<header>
|
13 |
+
<h1>Simple Notes</h1>
|
14 |
+
</header>
|
15 |
+
|
16 |
+
<main>
|
17 |
+
<section class="note-display">
|
18 |
+
<h2>Notes</h2>
|
19 |
+
<ul>
|
20 |
+
|
21 |
+
</ul>
|
22 |
+
</section>
|
23 |
+
<section class="new-note">
|
24 |
+
<h2>Enter a new note</h2>
|
25 |
+
<form>
|
26 |
+
<div>
|
27 |
+
<label for="title">Title</label>
|
28 |
+
<input id="title" type="text" required placeholder="Breathe">
|
29 |
+
</div>
|
30 |
+
<div>
|
31 |
+
<label for="date">Date</label>
|
32 |
+
<input id="date" type="text" required placeholder="Everyday">
|
33 |
+
</div>
|
34 |
+
<div>
|
35 |
+
<label for="content">Note</label>
|
36 |
+
<input id="content" type="text" required placeholder="Take 3 calm breathe everyday.">
|
37 |
+
</div>
|
38 |
+
<div>
|
39 |
+
<button id="newBtn">Create a new note</button>
|
40 |
+
</div>
|
41 |
+
</form>
|
42 |
+
</section>
|
43 |
+
</main>
|
44 |
+
|
45 |
+
<footer>
|
46 |
+
<p>
|
47 |
+
Trying to use IndexedDB to create mini database that is stored at the
|
48 |
+
client side.
|
49 |
+
</p>
|
50 |
+
<p>Content sourced from <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage#storing_complex_data_%E2%80%94_indexeddb">MDN</a>.
|
51 |
+
</p>
|
52 |
+
<p>Simple Notes by Chooi Chin Liew is marked with CC0 1.0 license.</p>
|
53 |
+
</footer>
|
54 |
+
</body>
|
55 |
+
</html>
|
simple-notes/index.js
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Create needed constants
|
2 |
+
const list = document.querySelector('ul');
|
3 |
+
const titleInput = document.querySelector('#title');
|
4 |
+
const dateInput = document.querySelector('#date');
|
5 |
+
const contentInput = document.querySelector('#content');
|
6 |
+
const form = document.querySelector('form');
|
7 |
+
const newBtn = document.querySelector('#newBtn');
|
8 |
+
// Create an instance of a db object for us to store the open database in
|
9 |
+
let db;
|
10 |
+
// Open our database; it is created if it doesn't already exist
|
11 |
+
// (see the upgradeneeded handler below)
|
12 |
+
const openRequest = window.indexedDB.open("notes_db", 1);
|
13 |
+
// error handler signifies that the database didn't open successfully
|
14 |
+
openRequest.addEventListener("error", () => console.error("Database failed to open"));
|
15 |
+
// Create a submit event handler so that when the form is submitted the addData() function is run
|
16 |
+
form.addEventListener("submit", addData);
|
17 |
+
// success handler signifies that the database opened successfully
|
18 |
+
openRequest.addEventListener("success", () => {
|
19 |
+
console.log("Database opened successfully");
|
20 |
+
// Store the opened database object in the db variable.
|
21 |
+
// This is used a lot below.
|
22 |
+
db = openRequest.result;
|
23 |
+
// Run the displayData() function to display the notes already in the IDB
|
24 |
+
displayData();
|
25 |
+
});
|
26 |
+
// Finally for this section, we'll add probably the most important event
|
27 |
+
// handler for setting up the database: upgradeneeded. This handler runs if the
|
28 |
+
// database has not already been set up, or if the database is opened with a
|
29 |
+
// bigger version number than the existing stored database (when performing an
|
30 |
+
// upgrade). Add the following code, below your previous handler:
|
31 |
+
// Set up the database tables if this has not already been done.
|
32 |
+
// Here we first grab a reference to the existing database from the result
|
33 |
+
// property of the event's target (e.target.result), which is the request
|
34 |
+
// object. This is equivalent to the line db = openRequest.result; inside the
|
35 |
+
// success event handler, but we need to do this separately here because the
|
36 |
+
// upgradeneeded event handler (if needed) will run before the success event
|
37 |
+
// handler, meaning that the db value wouldn't be available if we didn't do
|
38 |
+
// this.
|
39 |
+
openRequest.addEventListener("upgradeneeded", (e) => {
|
40 |
+
// Grab a reference to the opened database
|
41 |
+
db = e.target.result;
|
42 |
+
// Create an objectStore in our database to store notes and an
|
43 |
+
// auto-incrementing key
|
44 |
+
// An objectStore is similar to a 'table' in a relational database
|
45 |
+
const objectStore = db.createObjectStore("notes_os", {
|
46 |
+
keyPath: "id",
|
47 |
+
autoIncrement: true,
|
48 |
+
});
|
49 |
+
// Define what data items the objectStore will contain
|
50 |
+
objectStore.createIndex("title", "title", { unique: false });
|
51 |
+
objectStore.createIndex("content", "content", { unique: false });
|
52 |
+
objectStore.createIndex("date", "date", { unique: false });
|
53 |
+
// Print the outcome of the setup
|
54 |
+
console.log("Database setup completed.");
|
55 |
+
});
|
56 |
+
// Define the addData() function
|
57 |
+
function addData(e) {
|
58 |
+
// prevent default - we don't want the form to submit in the conventional way
|
59 |
+
e.preventDefault();
|
60 |
+
// grab the values entered into the form fields and store them in an object ready for being inserted into the DB
|
61 |
+
const newItem = {
|
62 |
+
title: titleInput.value,
|
63 |
+
content: contentInput.value,
|
64 |
+
date: dateInput.value
|
65 |
+
};
|
66 |
+
// open a read/write db transaction, ready for adding the data,
|
67 |
+
// name of the db: notes_os
|
68 |
+
// IDBDatabase.transaction()
|
69 |
+
const transaction = db.transaction(["notes_os"], "readwrite");
|
70 |
+
// call an object store that's already been added to the database
|
71 |
+
// IDBTransaction.objectStore()
|
72 |
+
const objectStore = transaction.objectStore("notes_os");
|
73 |
+
// Make a request to add our newItem object to the object store
|
74 |
+
// IDBObjectStore.add()
|
75 |
+
const addRequest = objectStore.add(newItem);
|
76 |
+
// If the transaction is successful
|
77 |
+
addRequest.addEventListener("success", () => {
|
78 |
+
// Clear the form, ready for adding the next entry
|
79 |
+
titleInput.value = "";
|
80 |
+
contentInput.value = "";
|
81 |
+
dateInput.value = "";
|
82 |
+
});
|
83 |
+
// Report on the success of the transaction completing, when everything is done
|
84 |
+
transaction.addEventListener("complete", () => {
|
85 |
+
console.log("Transaction completed: database modification finished.");
|
86 |
+
// update the display of data to show the newly added item, by running displayData() again.
|
87 |
+
displayData();
|
88 |
+
});
|
89 |
+
// If there is error in the transaction
|
90 |
+
transaction.addEventListener("error", () => console.log("Transaction not opened due to error"));
|
91 |
+
}
|
92 |
+
// Define the displayData() function
|
93 |
+
function displayData() {
|
94 |
+
// Here we empty the contents of the list element each time the display is updated
|
95 |
+
// If you didn't do this, you'd get duplicates listed each time a new note is added
|
96 |
+
while (list.firstChild) {
|
97 |
+
list.removeChild(list.firstChild);
|
98 |
+
}
|
99 |
+
// Open our object store and then get a cursor - which iterates through all the
|
100 |
+
// different data items in the store
|
101 |
+
const transaction = db.transaction("notes_os");
|
102 |
+
const objectStore = transaction.objectStore("notes_os");
|
103 |
+
// IDBObjectStore: openCursor(). Returns an IDBRequest object, and, in a
|
104 |
+
// separate thread, returns a new IDBCursorWithValue object. Used for
|
105 |
+
// iterating through an object store with a cursor.
|
106 |
+
objectStore.openCursor().addEventListener("success", (e) => {
|
107 |
+
// Get a reference to the cursor
|
108 |
+
const cursor = e.target.result;
|
109 |
+
// If there is still another data item to iterate through, keep running this code,
|
110 |
+
// In short:
|
111 |
+
// if (cursor) {
|
112 |
+
// cursor.value contains the current record being iterated through
|
113 |
+
// this is where you'd do something with the result
|
114 |
+
// cursor.continue();
|
115 |
+
//}
|
116 |
+
// else {
|
117 |
+
// no more results
|
118 |
+
//}
|
119 |
+
if (cursor) {
|
120 |
+
// Create a list item, h3, and p to put each data item inside when displaying it
|
121 |
+
// structure the HTML fragment, and append it inside the list
|
122 |
+
const listItem = document.createElement("li");
|
123 |
+
const h3 = document.createElement("h3");
|
124 |
+
const para = document.createElement("p");
|
125 |
+
listItem.appendChild(h3);
|
126 |
+
listItem.appendChild(para);
|
127 |
+
list.appendChild(listItem);
|
128 |
+
// Put the data from the cursor inside the h3 and para
|
129 |
+
h3.textContent = cursor.value.title;
|
130 |
+
para.textContent = `${cursor.value.date}: ${cursor.value.content}`;
|
131 |
+
// Store the ID of the data item inside an attribute on the listItem, so we know
|
132 |
+
// which item it corresponds to. This will be useful later when we want to delete items
|
133 |
+
listItem.setAttribute("data-note-id", cursor.value.id);
|
134 |
+
// Create a button and place it inside each listItem
|
135 |
+
const deleteBtn = document.createElement("button");
|
136 |
+
listItem.appendChild(deleteBtn);
|
137 |
+
deleteBtn.textContent = "Delete";
|
138 |
+
// Set an event handler so that when the button is clicked, the deleteItem()
|
139 |
+
// function is run
|
140 |
+
deleteBtn.addEventListener("click", deleteItem);
|
141 |
+
// Iterate to the next item in the cursor
|
142 |
+
cursor.continue();
|
143 |
+
}
|
144 |
+
else {
|
145 |
+
// Again, if list item is empty, display a 'No notes stored' message
|
146 |
+
if (!list.firstChild) {
|
147 |
+
const listItem = document.createElement("li");
|
148 |
+
listItem.textContent = "No notes stored.";
|
149 |
+
list.appendChild(listItem);
|
150 |
+
}
|
151 |
+
// if there are no more cursor items to iterate through, say so
|
152 |
+
console.log("Notes all displayed");
|
153 |
+
}
|
154 |
+
});
|
155 |
+
}
|
156 |
+
// Define the deleteItem() function
|
157 |
+
function deleteItem(e) {
|
158 |
+
// retrieve the name of the task we want to delete. We need
|
159 |
+
// to convert it to a number before trying to use it with IDB; IDB key
|
160 |
+
// values are type-sensitive.
|
161 |
+
const ulNode = e.target.parentNode;
|
162 |
+
// const noteId = Number(e.target.parentNode.getAttribute("data-note-id"));
|
163 |
+
// We do however need to pass the attribute through the global built-in
|
164 |
+
// Number() object as it is of datatype string, and therefore wouldn't be
|
165 |
+
// recognized by the database, which expects a number.
|
166 |
+
const noteId = Number(ulNode.getAttribute("data-note-id"));
|
167 |
+
// open a database transaction and delete the task, finding it using the id we retrieved above
|
168 |
+
const transaction = db.transaction(["notes_os"], "readwrite");
|
169 |
+
const objectStore = transaction.objectStore("notes_os");
|
170 |
+
const deleteRequest = objectStore.delete(noteId);
|
171 |
+
// report that the data item has been deleted
|
172 |
+
transaction.addEventListener("complete", () => {
|
173 |
+
// delete the parent of the button
|
174 |
+
// which is the list item, so it is no longer displayed
|
175 |
+
//e.target.parentNode.parentNode.removeChild(e.target.parentNode);
|
176 |
+
ulNode.parentNode.removeChild(ulNode);
|
177 |
+
console.log(`Note ${noteId} deleted.`);
|
178 |
+
// Again, if list item is empty, display a 'No notes stored' message
|
179 |
+
if (!list.firstChild) {
|
180 |
+
const listItem = document.createElement("li");
|
181 |
+
listItem.textContent = "No notes stored.";
|
182 |
+
list.appendChild(listItem);
|
183 |
+
}
|
184 |
+
});
|
185 |
+
}
|
simple-notes/index.ts
ADDED
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Create needed constants
|
2 |
+
const list = document.querySelector('ul') as HTMLUListElement;
|
3 |
+
const titleInput = document.querySelector('#title') as HTMLInputElement;
|
4 |
+
const dateInput = document.querySelector('#date') as HTMLInputElement;
|
5 |
+
const contentInput = document.querySelector('#content') as HTMLInputElement;
|
6 |
+
const form = document.querySelector('form') as HTMLFormElement;
|
7 |
+
const newBtn = document.querySelector('#newBtn') as HTMLButtonElement;
|
8 |
+
|
9 |
+
// Create an instance of a db object for us to store the open database in
|
10 |
+
let db : IDBDatabase;
|
11 |
+
|
12 |
+
// Open our database; it is created if it doesn't already exist
|
13 |
+
// (see the upgradeneeded handler below)
|
14 |
+
const openRequest = window.indexedDB.open("notes_db", 1) as IDBOpenDBRequest;
|
15 |
+
|
16 |
+
// error handler signifies that the database didn't open successfully
|
17 |
+
openRequest.addEventListener("error", () =>
|
18 |
+
console.error("Database failed to open"),
|
19 |
+
);
|
20 |
+
// Create a submit event handler so that when the form is submitted the addData() function is run
|
21 |
+
form.addEventListener("submit", addData);
|
22 |
+
|
23 |
+
|
24 |
+
// success handler signifies that the database opened successfully
|
25 |
+
openRequest.addEventListener("success", () => {
|
26 |
+
console.log("Database opened successfully");
|
27 |
+
// Store the opened database object in the db variable.
|
28 |
+
// This is used a lot below.
|
29 |
+
db = openRequest.result;
|
30 |
+
// Run the displayData() function to display the notes already in the IDB
|
31 |
+
displayData();
|
32 |
+
});
|
33 |
+
|
34 |
+
// Finally for this section, we'll add probably the most important event
|
35 |
+
// handler for setting up the database: upgradeneeded. This handler runs if the
|
36 |
+
// database has not already been set up, or if the database is opened with a
|
37 |
+
// bigger version number than the existing stored database (when performing an
|
38 |
+
// upgrade). Add the following code, below your previous handler:
|
39 |
+
// Set up the database tables if this has not already been done.
|
40 |
+
// Here we first grab a reference to the existing database from the result
|
41 |
+
// property of the event's target (e.target.result), which is the request
|
42 |
+
// object. This is equivalent to the line db = openRequest.result; inside the
|
43 |
+
// success event handler, but we need to do this separately here because the
|
44 |
+
// upgradeneeded event handler (if needed) will run before the success event
|
45 |
+
// handler, meaning that the db value wouldn't be available if we didn't do
|
46 |
+
// this.
|
47 |
+
openRequest.addEventListener("upgradeneeded", (e: Event) => {
|
48 |
+
// Grab a reference to the opened database
|
49 |
+
db = (e.target as IDBOpenDBRequest).result as IDBDatabase;
|
50 |
+
|
51 |
+
// Create an objectStore in our database to store notes and an
|
52 |
+
// auto-incrementing key
|
53 |
+
// An objectStore is similar to a 'table' in a relational database
|
54 |
+
const objectStore = db.createObjectStore("notes_os", {
|
55 |
+
keyPath: "id",
|
56 |
+
autoIncrement: true,
|
57 |
+
});
|
58 |
+
|
59 |
+
// Define what data items the objectStore will contain
|
60 |
+
objectStore.createIndex("title", "title", { unique: false });
|
61 |
+
objectStore.createIndex("content", "content", { unique: false });
|
62 |
+
objectStore.createIndex("date", "date", {unique: false});
|
63 |
+
// Print the outcome of the setup
|
64 |
+
console.log("Database setup completed.");
|
65 |
+
});
|
66 |
+
|
67 |
+
// Define the addData() function
|
68 |
+
function addData(e: Event) {
|
69 |
+
// prevent default - we don't want the form to submit in the conventional way
|
70 |
+
e.preventDefault();
|
71 |
+
|
72 |
+
// grab the values entered into the form fields and store them in an object ready for being inserted into the DB
|
73 |
+
const newItem = {
|
74 |
+
title: titleInput.value,
|
75 |
+
content: contentInput.value,
|
76 |
+
date: dateInput.value
|
77 |
+
};
|
78 |
+
|
79 |
+
// open a read/write db transaction, ready for adding the data,
|
80 |
+
// name of the db: notes_os
|
81 |
+
// IDBDatabase.transaction()
|
82 |
+
const transaction = db.transaction(["notes_os"], "readwrite");
|
83 |
+
|
84 |
+
// call an object store that's already been added to the database
|
85 |
+
// IDBTransaction.objectStore()
|
86 |
+
const objectStore = transaction.objectStore("notes_os");
|
87 |
+
|
88 |
+
// Make a request to add our newItem object to the object store
|
89 |
+
// IDBObjectStore.add()
|
90 |
+
const addRequest = objectStore.add(newItem);
|
91 |
+
|
92 |
+
// If the transaction is successful
|
93 |
+
addRequest.addEventListener("success", () => {
|
94 |
+
// Clear the form, ready for adding the next entry
|
95 |
+
titleInput.value = "";
|
96 |
+
contentInput.value = "";
|
97 |
+
dateInput.value = "";
|
98 |
+
});
|
99 |
+
|
100 |
+
// Report on the success of the transaction completing, when everything is done
|
101 |
+
transaction.addEventListener("complete", () => {
|
102 |
+
console.log("Transaction completed: database modification finished.");
|
103 |
+
|
104 |
+
// update the display of data to show the newly added item, by running displayData() again.
|
105 |
+
displayData();
|
106 |
+
});
|
107 |
+
|
108 |
+
// If there is error in the transaction
|
109 |
+
transaction.addEventListener("error", () =>
|
110 |
+
console.log("Transaction not opened due to error"),
|
111 |
+
);
|
112 |
+
}
|
113 |
+
|
114 |
+
// Define the displayData() function
|
115 |
+
function displayData() {
|
116 |
+
// Here we empty the contents of the list element each time the display is updated
|
117 |
+
// If you didn't do this, you'd get duplicates listed each time a new note is added
|
118 |
+
while (list.firstChild) {
|
119 |
+
list.removeChild(list.firstChild);
|
120 |
+
}
|
121 |
+
|
122 |
+
// Open our object store and then get a cursor - which iterates through all the
|
123 |
+
// different data items in the store
|
124 |
+
const transaction = db.transaction("notes_os");
|
125 |
+
const objectStore = transaction.objectStore("notes_os");
|
126 |
+
// IDBObjectStore: openCursor(). Returns an IDBRequest object, and, in a
|
127 |
+
// separate thread, returns a new IDBCursorWithValue object. Used for
|
128 |
+
// iterating through an object store with a cursor.
|
129 |
+
objectStore.openCursor().addEventListener("success", (e) => {
|
130 |
+
// Get a reference to the cursor
|
131 |
+
const cursor = (e.target as IDBRequest).result as IDBCursorWithValue;
|
132 |
+
|
133 |
+
// If there is still another data item to iterate through, keep running this code,
|
134 |
+
// In short:
|
135 |
+
// if (cursor) {
|
136 |
+
// cursor.value contains the current record being iterated through
|
137 |
+
// this is where you'd do something with the result
|
138 |
+
// cursor.continue();
|
139 |
+
//}
|
140 |
+
// else {
|
141 |
+
// no more results
|
142 |
+
//}
|
143 |
+
if (cursor) {
|
144 |
+
// Create a list item, h3, and p to put each data item inside when displaying it
|
145 |
+
// structure the HTML fragment, and append it inside the list
|
146 |
+
const listItem = document.createElement("li");
|
147 |
+
const h3 = document.createElement("h3");
|
148 |
+
const para = document.createElement("p");
|
149 |
+
|
150 |
+
listItem.appendChild(h3);
|
151 |
+
listItem.appendChild(para);
|
152 |
+
list.appendChild(listItem);
|
153 |
+
|
154 |
+
// Put the data from the cursor inside the h3 and para
|
155 |
+
h3.textContent = cursor.value.title;
|
156 |
+
para.textContent = `${cursor.value.date}: ${cursor.value.content}`;
|
157 |
+
|
158 |
+
// Store the ID of the data item inside an attribute on the listItem, so we know
|
159 |
+
// which item it corresponds to. This will be useful later when we want to delete items
|
160 |
+
listItem.setAttribute("data-note-id", cursor.value.id);
|
161 |
+
|
162 |
+
// Create a button and place it inside each listItem
|
163 |
+
const deleteBtn = document.createElement("button");
|
164 |
+
listItem.appendChild(deleteBtn);
|
165 |
+
deleteBtn.textContent = "Delete";
|
166 |
+
|
167 |
+
// Set an event handler so that when the button is clicked, the deleteItem()
|
168 |
+
// function is run
|
169 |
+
deleteBtn.addEventListener("click", deleteItem);
|
170 |
+
|
171 |
+
// Iterate to the next item in the cursor
|
172 |
+
cursor.continue();
|
173 |
+
} else {
|
174 |
+
// Again, if list item is empty, display a 'No notes stored' message
|
175 |
+
if (!list.firstChild) {
|
176 |
+
const listItem = document.createElement("li");
|
177 |
+
listItem.textContent = "No notes stored.";
|
178 |
+
list.appendChild(listItem);
|
179 |
+
}
|
180 |
+
// if there are no more cursor items to iterate through, say so
|
181 |
+
console.log("Notes all displayed");
|
182 |
+
}
|
183 |
+
});
|
184 |
+
}
|
185 |
+
|
186 |
+
// Define the deleteItem() function
|
187 |
+
function deleteItem(e: Event) {
|
188 |
+
// retrieve the name of the task we want to delete. We need
|
189 |
+
// to convert it to a number before trying to use it with IDB; IDB key
|
190 |
+
// values are type-sensitive.
|
191 |
+
const ulNode = (e.target as HTMLLIElement).parentNode as HTMLUListElement;
|
192 |
+
// const noteId = Number(e.target.parentNode.getAttribute("data-note-id"));
|
193 |
+
// We do however need to pass the attribute through the global built-in
|
194 |
+
// Number() object as it is of datatype string, and therefore wouldn't be
|
195 |
+
// recognized by the database, which expects a number.
|
196 |
+
const noteId = Number(ulNode.getAttribute("data-note-id"));
|
197 |
+
|
198 |
+
// open a database transaction and delete the task, finding it using the id we retrieved above
|
199 |
+
const transaction = db.transaction(["notes_os"], "readwrite");
|
200 |
+
const objectStore = transaction.objectStore("notes_os");
|
201 |
+
const deleteRequest = objectStore.delete(noteId);
|
202 |
+
|
203 |
+
// report that the data item has been deleted
|
204 |
+
transaction.addEventListener("complete", () => {
|
205 |
+
// delete the parent of the button
|
206 |
+
// which is the list item, so it is no longer displayed
|
207 |
+
//e.target.parentNode.parentNode.removeChild(e.target.parentNode);
|
208 |
+
ulNode.parentNode.removeChild(ulNode);
|
209 |
+
console.log(`Note ${noteId} deleted.`);
|
210 |
+
|
211 |
+
// Again, if list item is empty, display a 'No notes stored' message
|
212 |
+
if (!list.firstChild) {
|
213 |
+
const listItem = document.createElement("li");
|
214 |
+
listItem.textContent = "No notes stored.";
|
215 |
+
list.appendChild(listItem);
|
216 |
+
}
|
217 |
+
});
|
218 |
+
}
|
219 |
+
|
220 |
+
|
221 |
+
|
simple-notes/style.css
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
/* Mobile first design */
|
3 |
+
html {
|
4 |
+
font-family: sans-serif;
|
5 |
+
}
|
6 |
+
|
7 |
+
body {
|
8 |
+
max-width: 100%;
|
9 |
+
}
|
10 |
+
header, footer {
|
11 |
+
background-color: green;
|
12 |
+
color: white;
|
13 |
+
padding: 0 20px;
|
14 |
+
}
|
15 |
+
header {
|
16 |
+
line-height: 80px;
|
17 |
+
}
|
18 |
+
footer {
|
19 |
+
font-size: 10pt;
|
20 |
+
}
|
21 |
+
form div {
|
22 |
+
display: grid;
|
23 |
+
grid-template-columns: 1fr 4fr;
|
24 |
+
grid-template-rows: repeat(3, auto);
|
25 |
+
justify-content: space-evenly;
|
26 |
+
}
|
27 |
+
/* long spacious button */
|
28 |
+
#newBtn {
|
29 |
+
grid-column: 2/5;
|
30 |
+
}
|
31 |
+
form div label {
|
32 |
+
text-align: right;
|
33 |
+
margin-right: 1em;
|
34 |
+
}
|
35 |
+
.new-note, .note-display {
|
36 |
+
padding: 20px;
|
37 |
+
}
|
38 |
+
.new-note {
|
39 |
+
background: lightgreen;
|
40 |
+
}
|
41 |
+
|
42 |
+
h1 {
|
43 |
+
margin: 0;
|
44 |
+
}
|
45 |
+
|
46 |
+
ul {
|
47 |
+
list-style-type: none;
|
48 |
+
}
|
49 |
+
|
50 |
+
div {
|
51 |
+
margin-bottom: 10px;
|
52 |
+
}
|
53 |
+
|
54 |
+
/* For wider display */
|
55 |
+
@media (min-width: 600px) {
|
56 |
+
body {
|
57 |
+
margin: 0 auto;
|
58 |
+
max-width: 800px;
|
59 |
+
}
|
60 |
+
}
|
simple-notes/tsconfig.json
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"target": "ESNext",
|
4 |
+
"module": "ESNext",
|
5 |
+
}
|
6 |
+
}
|