<!DOCTYPE html> <html> <head> <!-- Based on http://web-engineering.info/JsFrontendApp by Gerd Wagner --> <title>Javascript Example UI</title> <style> body { text-align: justify; font-family: arial, sans-serif; color: saddlebrown; } button { color: saddlebrown; background: beige; border: none; height: 100px; width: 300px; font-size: 14pt; text-align: center; } button.testing { background: cornsilk; } button.inpanel { border-radius: 20px; height: 50px; width: 120px; } .bigcap { font-size: 24pt; } </style> <script> "use strict" class Main { constructor () { //*3 Attach the listeners this._bookDB = new BookDB() // Install callbacks for main panel createButton.addEventListener("click", () => this._createButtonCB()) //*3 retrieveButton.addEventListener("click", () => this._retrieveButtonCB()) //*3 updateButton.addEventListener("click", () => this._updateButtonCB()) //*3 deleteButton.addEventListener("click", () => this._deleteButtonCB()) //*3 // These are mainly for testing, they just do the action, no transient panel clearButton.addEventListener("click", () => this._clearDataCB()) generateButton.addEventListener("click", () => this._generateTestDataCB()) // Callbacks for widgets inside the transient panels createSaveButton.addEventListener("click", () => this._createSaveButtonCB()) //*3 retrieveDoneButton.addEventListener("click", () => this._retrieveDoneButtonCB()) //*3 updateSaveButton.addEventListener("click", () => this._updateSaveButtonCB()) //*3 updateSelectBook.addEventListener("change", () => this._updateSelectBookCB()) //*3 deleteDoneButton.addEventListener("click", () => this._deleteDoneButtonCB()) //*3 } /* * Callback routines for various UI actions */ /* * Just displays the panel, could have been a one-line anonymous function in addEventListener, * but all the callbacks are here in separate functions, for uniformity/clarity */ _createButtonCB () { //*3 this._showPanel ("createPanel"); //*3 } /* * Called from within the panel, does the actual save operation */ _createSaveButtonCB () { //*4 Called from within the panel, does the actual save operation this._bookDB.add (new Book (createForm.isbn.value, createForm.title.value, createForm.year.value)) //*4 createForm.reset() //*4 this._showPanel (null) //*4 } /* * Displays the panel and fills it up with the data */ _retrieveButtonCB () { //*5 Displays the panel and fills it up with the data this._showPanel ("retrievePanel") //*5 // Clear the table display retrieveTable.innerHTML = ""; //*5 // Load all book objects for (let book of this._bookDB.retrieveAll()) { //*5 // For each book, create a table row with a cell for each of its attributes let row = retrieveTable.insertRow(); //*5 row.insertCell(-1).textContent = book.isbn //*5 row.insertCell(-1).textContent = book.title //*5 row.insertCell(-1).textContent = book.year //*5 } } /* * Just gets rid of the panel, * if we didn't do that, we'd want to update it whenever other commands change the DB */ _retrieveDoneButtonCB () { //*6 Just gets rid of the panel this._showPanel (null) //*6 } /* * Pops up the panel, also sets up its selection list for user to choose */ _updateButtonCB () { //*7 Pops up the panel this._showPanel ("updatePanel") // Set the panel up for selecting which book to update updateSelectBook.innerHTML = '<option value=""> --- </option>' //*7 // Populate the selection list with books for (let book of this._bookDB.retrieveAll()) { //*7 let option = document.createElement("option"); option.text = book.title; //*7 option.value = book.isbn; //*7 updateSelectBook.add( option, null); //*7 } } /* * Whenever user chooses a book from selection list, * we populate our window with data from that book */ _updateSelectBookCB () { //*8 Whenever user chooses from selection list let book = this._bookDB.retrieveBook (updateSelectBook.value) updateForm.isbn.value = book.isbn; //*8 updateForm.title.value = book.title; //*8 updateForm.year.value = book.year; //*8 } /* * Enter the change into the DB * NB if we didn't hide our panel at the end, we should update its selection widget */ _updateSaveButtonCB () { //*9 Enter the change into the DB let isbn = updateSelectBook.value; this._bookDB.update (updateForm.isbn.value, updateForm.title.value, updateForm.year.value) //*9 updateForm.reset(); this._showPanel (null); } /* * Pops up the panel, also sets up the selection list for user to choose */ _deleteButtonCB () { this._showPanel ("deletePanel") // Set the panel up for selecting which book to delete deleteSelectBook.innerHTML = '<option value=""> --- </option>' // Populate the selection list with books for (let book of this._bookDB.retrieveAll()) { let option = document.createElement("option"); option.text = book.title; option.value = book.isbn; deleteSelectBook.add( option, null); } } /* * Actually delete the entry * NB if we didn't hide our panel at the end, we should update its selection widget */ _deleteDoneButtonCB () { //*9 let isbn = deleteSelectBook.value; //*9 if (isbn) this._bookDB.deleteBook (isbn); //*9 this._showPanel (null); } /* * For testing: create and save some test data */ _generateTestDataCB () { this._bookDB.add (new Book ("006251587X", "Weaving the Web", 2000)) this._bookDB.add (new Book ("0465026567", "Godel, Escher, Bach", 1999)) this._bookDB.add (new Book ("0465030793", "I Am A Strange Loop", 2008)) this._showPanel (null) } /* * For testing: clear data */ _clearDataCB () { if (confirm("Do you really want to delete all book data?")) { this._bookDB = new BookDB() this._showPanel (null) // Mainly in case retrieve display was visible } } /* * Internal routine, * shows one of the transient panels whose id is given (or none if arg = null) * and hides all the rest */ _showPanel (panel) { for (let p of ["createPanel", "retrievePanel", "updatePanel", "deletePanel"]) { document.getElementById(p).style.display = "none"; } if (panel) document.getElementById(panel).style.display = "block"; } } /* * Class that holds data about one book */ class Book { //*1 Book object constructor (isbn, title, year) { //*1 this._isbn = isbn; //*1 this._title = title; //*1 this._year = year; //*1 } update (title, year) { //*1 if (this._title !== title) this._title = title; //*1 let yearInt = parseInt( year); if (this._year !== yearInt) this._year = yearInt; //*1 } // Javascript getter functions get isbn() {return this._isbn} //*1 get title() {return this._title} //*1 get year() {return this._year} //*1 } /* * The database of books, an array indexed by isbn */ class BookDB { //*2 Database of books constructor () { //*2 this._db = {}; //*2 } // Add given book to our DB add (book) { //*2 this._db[book.isbn] = book; } // Update an existing book entry update (isbn, title, year) { //*2 let book = this._db[isbn]; book.update (title, year) } // Retrieve one book retrieveBook (isbn) { //*2 if (this._db[isbn]) { return this._db[isbn]; } else { console.log("There is no book with ISBN " + isbn + " in the database"); return null } } // Return all our data, converted into an array retrieveAll () { //*2 let ans = [] //*2 for (let key of Object.keys(this._db)) { //*2 ans.push (this._db[key]) //*2 } return ans; } // Delete a book deleteBook (isbn) { //*2 if (this._db[isbn]) { delete this._db[isbn]; } else { console.log("There is no book with ISBN " + isbn + " in the database"); } } } /* * Install all our button callbacks */ window.onload = function () { new Main (); } </script> </head> <body> <div> <h1>Book Catalogue UI Demo</h1> <hr/> <button type="button" id="createButton"><span class="bigcap">C</span>reate a new book record</button> <button type="button" id="retrieveButton"><span class="bigcap">R</span>etrieve and list all book records</button> <button type="button" id="updateButton"><span class="bigcap">U</span>pdate a book record</button> <button type="button" id="deleteButton"><span class="bigcap">D</span>elete a book record</button> <hr/> <!-- These are for testing the system --> <button type="button" class="testing" id="clearButton">Clear database</button> <button type="button" class="testing" id="generateButton">Generate test data</button> <div> <div id="createPanel" style="display:none"> <!-- These panels stay invisible until we need them --> <br/><hr/><br/> <h2>Create a new book record</h2> <form id="createForm"> <div><label>ISBN: <input name="isbn" /></label></div> <div><label>Title: <input name="title" /></label></div> <div><label>Year: <input name="year" /></label></div> <div><button type="button" class="inpanel" id="createSaveButton">Save</button></div> </form> </div> <!-- The rest of these are initialy hidden, displayed as needed --> <div id="retrievePanel" style="display:none"> <br/><hr/><br/> <h2>Retrieve and list all book records</h2> <table> <thead><tr><th>ISBN</th><th>Title</th><th>Year</th></tr></thead> <tbody id="retrieveTable"></tbody> </table> <button type="button" class="inpanel" id="retrieveDoneButton">Done</button> </div> <div id="updatePanel" style="display:none"> <br/><hr/><br/> <h2>Update a book record</h2> <form id="updateForm"> <div> <label>Select book: <select id="updateSelectBook"></select> </label> </div> <div><label>ISBN: <output name="isbn" /></label></div> <div><label>Title: <input name="title" /></label></div> <div><label>Year: <input name="year" /></label></div> <div><button type="button" class="inpanel" id="updateSaveButton">Save update</button></div> </form> </div> <div id="deletePanel" style="display:none"> <br/><hr/><br/> <h2>Delete a book record</h2> <form id="deleteForm"> <div> <label>Select book to delete: <select id="deleteSelectBook"></select> </label> </div> <div><button type="button" class="inpanel" id="deleteDoneButton">Delete</button></div> </form> </div> </body> </html>