// Create needed constants const list = document.querySelector('ul') as HTMLUListElement; const titleInput = document.querySelector('#title') as HTMLInputElement; const dateInput = document.querySelector('#date') as HTMLInputElement; const contentInput = document.querySelector('#content') as HTMLInputElement; const form = document.querySelector('form') as HTMLFormElement; const newBtn = document.querySelector('#newBtn') as HTMLButtonElement; // Create an instance of a db object for us to store the open database in let db : IDBDatabase; // Open our database; it is created if it doesn't already exist // (see the upgradeneeded handler below) const openRequest = window.indexedDB.open("notes_db", 1) as IDBOpenDBRequest; // error handler signifies that the database didn't open successfully openRequest.addEventListener("error", () => console.error("Database failed to open"), ); // Create a submit event handler so that when the form is submitted the addData() function is run form.addEventListener("submit", addData); // success handler signifies that the database opened successfully openRequest.addEventListener("success", () => { console.log("Database opened successfully"); // Store the opened database object in the db variable. // This is used a lot below. db = openRequest.result; // Run the displayData() function to display the notes already in the IDB displayData(); }); // Finally for this section, we'll add probably the most important event // handler for setting up the database: upgradeneeded. This handler runs if the // database has not already been set up, or if the database is opened with a // bigger version number than the existing stored database (when performing an // upgrade). Add the following code, below your previous handler: // Set up the database tables if this has not already been done. // Here we first grab a reference to the existing database from the result // property of the event's target (e.target.result), which is the request // object. This is equivalent to the line db = openRequest.result; inside the // success event handler, but we need to do this separately here because the // upgradeneeded event handler (if needed) will run before the success event // handler, meaning that the db value wouldn't be available if we didn't do // this. openRequest.addEventListener("upgradeneeded", (e: Event) => { // Grab a reference to the opened database db = (e.target as IDBOpenDBRequest).result as IDBDatabase; // Create an objectStore in our database to store notes and an // auto-incrementing key // An objectStore is similar to a 'table' in a relational database const objectStore = db.createObjectStore("notes_os", { keyPath: "id", autoIncrement: true, }); // Define what data items the objectStore will contain objectStore.createIndex("title", "title", { unique: false }); objectStore.createIndex("content", "content", { unique: false }); objectStore.createIndex("date", "date", {unique: false}); // Print the outcome of the setup console.log("Database setup completed."); }); // Define the addData() function function addData(e: Event) { // prevent default - we don't want the form to submit in the conventional way e.preventDefault(); // grab the values entered into the form fields and store them in an object ready for being inserted into the DB const newItem = { title: titleInput.value, content: contentInput.value, date: dateInput.value }; // open a read/write db transaction, ready for adding the data, // name of the db: notes_os // IDBDatabase.transaction() const transaction = db.transaction(["notes_os"], "readwrite"); // call an object store that's already been added to the database // IDBTransaction.objectStore() const objectStore = transaction.objectStore("notes_os"); // Make a request to add our newItem object to the object store // IDBObjectStore.add() const addRequest = objectStore.add(newItem); // If the transaction is successful addRequest.addEventListener("success", () => { // Clear the form, ready for adding the next entry titleInput.value = ""; contentInput.value = ""; dateInput.value = ""; }); // Report on the success of the transaction completing, when everything is done transaction.addEventListener("complete", () => { console.log("Transaction completed: database modification finished."); // update the display of data to show the newly added item, by running displayData() again. displayData(); }); // If there is error in the transaction transaction.addEventListener("error", () => console.log("Transaction not opened due to error"), ); } // Define the displayData() function function displayData() { // Here we empty the contents of the list element each time the display is updated // If you didn't do this, you'd get duplicates listed each time a new note is added while (list.firstChild) { list.removeChild(list.firstChild); } // Open our object store and then get a cursor - which iterates through all the // different data items in the store const transaction = db.transaction("notes_os"); const objectStore = transaction.objectStore("notes_os"); // IDBObjectStore: openCursor(). Returns an IDBRequest object, and, in a // separate thread, returns a new IDBCursorWithValue object. Used for // iterating through an object store with a cursor. objectStore.openCursor().addEventListener("success", (e) => { // Get a reference to the cursor const cursor = (e.target as IDBRequest).result as IDBCursorWithValue; // If there is still another data item to iterate through, keep running this code, // In short: // if (cursor) { // cursor.value contains the current record being iterated through // this is where you'd do something with the result // cursor.continue(); //} // else { // no more results //} if (cursor) { // Create a list item, h3, and p to put each data item inside when displaying it // structure the HTML fragment, and append it inside the list const listItem = document.createElement("li"); const h3 = document.createElement("h3"); const para = document.createElement("p"); listItem.appendChild(h3); listItem.appendChild(para); list.appendChild(listItem); // Put the data from the cursor inside the h3 and para h3.textContent = cursor.value.title; para.textContent = `${cursor.value.date}: ${cursor.value.content}`; // Store the ID of the data item inside an attribute on the listItem, so we know // which item it corresponds to. This will be useful later when we want to delete items listItem.setAttribute("data-note-id", cursor.value.id); // Create a button and place it inside each listItem const deleteBtn = document.createElement("button"); listItem.appendChild(deleteBtn); deleteBtn.textContent = "Delete"; // Set an event handler so that when the button is clicked, the deleteItem() // function is run deleteBtn.addEventListener("click", deleteItem); // Iterate to the next item in the cursor cursor.continue(); } else { // Again, if list item is empty, display a 'No notes stored' message if (!list.firstChild) { const listItem = document.createElement("li"); listItem.textContent = "No notes stored."; list.appendChild(listItem); } // if there are no more cursor items to iterate through, say so console.log("Notes all displayed"); } }); } // Define the deleteItem() function function deleteItem(e: Event) { // retrieve the name of the task we want to delete. We need // to convert it to a number before trying to use it with IDB; IDB key // values are type-sensitive. const ulNode = (e.target as HTMLLIElement).parentNode as HTMLUListElement; // const noteId = Number(e.target.parentNode.getAttribute("data-note-id")); // We do however need to pass the attribute through the global built-in // Number() object as it is of datatype string, and therefore wouldn't be // recognized by the database, which expects a number. const noteId = Number(ulNode.getAttribute("data-note-id")); // open a database transaction and delete the task, finding it using the id we retrieved above const transaction = db.transaction(["notes_os"], "readwrite"); const objectStore = transaction.objectStore("notes_os"); const deleteRequest = objectStore.delete(noteId); // report that the data item has been deleted transaction.addEventListener("complete", () => { // delete the parent of the button // which is the list item, so it is no longer displayed //e.target.parentNode.parentNode.removeChild(e.target.parentNode); ulNode.parentNode.removeChild(ulNode); console.log(`Note ${noteId} deleted.`); // Again, if list item is empty, display a 'No notes stored' message if (!list.firstChild) { const listItem = document.createElement("li"); listItem.textContent = "No notes stored."; list.appendChild(listItem); } }); }