The user interface (UI) consists of a start page index.html
that allows navigating to data management UI pages, one for each object type (in our example, books.html
and publishers.html
). Each of these data management UI pages contains 5 sections: a Manage section, like Manage books, with a menu for choosing a CRUD use case, and a section for each CRUD use case, like Retrieve/list all books, Create book, Update book and Delete book, such that only one of them is displayed at any time (for instance, by setting the CSS property display:none
for all others).
For initializing a data management use case, the required data (for instance, all publisher and book records) have to be loaded from persistent storage. This is performed in a controller procedure such as pl.c.books.manage.initialize
in c/books.js
with the following code:
pl.c.books.manage = { initialize: function () { Publisher.retrieveAll(); Book.retrieveAll(); pl.v.books.manage.setUpUserInterface(); } };
The initialize
method for managing book data first loads the publishers table and the books table since the book data management UI needs to show their data. Then the book data management menu is rendered by calling the setUpUserInterface
procedure.
In our example, we have only one reference property, Book::publisher
, which is functional and optional. For showing information about the optional publisher of a book in the Retrieve/list all use case, the corresponding cell in the HTML table is filled with the name of the publisher, if there is any:
pl.v.books.retrieveAndListAll = {
setupUserInterface: function () {
const tableBodyEl = document.querySelector(
"section#Book-R>table>tbody");
tableBodyEl.innerHTML = ""; // drop old contents
for (let key of Object.keys( Book.instances)) {
const book = Book.instances[key];
const row = tableBodyEl.insertRow(-1);
row.insertCell(-1).textContent = book.isbn;
row.insertCell(-1).textContent = book.title;
row.insertCell(-1).textContent = book.year;
// if the book has a publisher, show its name
row.insertCell(-1).textContent =
book.publisher ? book.publisher.name : "";
}
document.getElementById("Book-M").style.display = "none";
document.getElementById("Book-R").style.display = "block";
}
};
For a multi-valued reference property, the table cell would have to be filled with a list of all associated objects referenced by the property.
For allowing to select objects to be associated with the currently edited object from in the Create and Update use cases, an HTML selection list (i.e., a select
element) is populated with option
elements formed from the instances of the associated object type with the help of a utility method fillSelectWithOptions
. The HTML select
element is defined in the books.html
view file:
<section id="Book-C" class="UI-Page"> <h1>Public Library: Create a new book record</h1> <form> ... <div class="select-one"> <label>Publisher: <select name="selectPublisher"></select></label> </div> ... </form> </section>
The Create user interface is set up by the following procedure:
pl.v.books.create = { setupUserInterface: function () { const formEl = document.querySelector("section#Book-C > form"), selectPublisherEl = formEl.selectPublisher, saveButton = formEl.commit; // add event listeners for responsive validation formEl.isbn.addEventListener("input", function () { formEl.isbn.setCustomValidity( Book.checkIsbnAsId( formEl.isbn.value).message); }); // set up a single selection list for selecting a publisher util.fillSelectWithOptions( selectPublisherEl, Publisher.instances, "name"); // define event handler for submitButton click events saveButton.addEventListener("click", this.handleSaveButtonClickEvent); // define event handler for neutralizing the submit event formEl.addEventListener("submit", function (e) { e.preventDefault(); formEl.reset(); }); // replace the manage form with the create form document.getElementById("Book-M").style.display = "none"; document.getElementById("Book-C").style.display = "block"; formEl.reset(); }, handleSaveButtonClickEvent: function () { ... } };
When the user clicks (or touches) the save button, all form control values, including the value of the select
control, are copied to a slots
list, which is used as the argument for invoking the add
method after all form fields have been checked for validity, as shown in the following program listing:
handleSaveButtonClickEvent: function () { const formEl = document.querySelector("section#Book-C > form"); const slots = { isbn: formEl.isbn.value, title: formEl.title.value, year: formEl.year.value, publisher_id: formEl.selectPublisher.value }; // validate all form controls and show error messages formEl.isbn.setCustomValidity( Book.checkIsbnAsId( slots.isbn).message); /* ... (do the same with title and year) */ // save the input data only if all form fields are valid if (formEl.checkValidity()) { Book.add( slots); } }
The setupUserInterface
code for the update book use case is similar.