5. The View and Controller Layers

The user interface (UI) consists of a start page for navigating to the 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, such as manage books, list books, create book, update book and delete book, such that only one of them is displayed at any time (by setting the CSS property display:none for all others).

5.1. Initialize the app

For initializing the data management use cases, the required data (all publisher and book records) are loaded from persistent storage. This is performed in a controller procedure such as pl.ctrl.books.manage.initialize in ctrl/books.js with the following code:

pl.ctrl.books.manage = {
  initialize: function () {
    Publisher.loadAll();
    Book.loadAll();
    pl.view.books.manage.setUpUserInterface();
  }
};

The initialize method for managing book data loads the publisher table and the book table since the book data management UI needs to provide selection list for both object types. Then the menu for book data managemetn options is set up by the setUpUserInterface method.

5.2. Show information about associated objects in the List Objects use case

In our example we have only one reference property, Book::publisher, which is functional. For showing information about the optional publisher of a book in the list books use case, the corresponding cell in the HTML table is filled with the name of the publisher, if there is any:

pl.view.books.list = {
  setupUserInterface: function () {
    var tableBodyEl = document.querySelector(
                      "section#Book-R>table>tbody");
    var keys = Object.keys( Book.instances);
    var row=null, listEl=null, book=null;
    tableBodyEl.innerHTML = "";
    for (var i=0; i < keys.length; i++) {
      book = Book.instances[keys[i]];
      row = tableBodyEl.insertRow(-1);
      row.insertCell(-1).textContent = book.isbn;
      row.insertCell(-1).textContent = book.title;
      row.insertCell(-1).textContent = book.year;
      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.

5.3. Allow selecting associated objects in the create and update use cases

For allowing to select objects to be associated with the currently edited object from a list in the create and update use cases, an HTML selection list (a select element) is populated with the instances of the associated object type with the help of a utility method fillSelectWithOptions. In the case of the create book use case, the UI is set up by the following procedure:

pl.view.books.create = {
  setupUserInterface: function () {
    var formEl = document.querySelector("section#Book-C > form"),
        publisherSelectEl = formEl.selectPublisher,
        submitButton = formEl.commit;
    // define event handlers for responsive validation 
    formEl.isbn.addEventListener("input", function () {
      formEl.isbn.setCustomValidity( 
          Book.checkIsbnAsId( formEl.isbn.value).message);
    });
    // set up the publisher selection list
    util.fillSelectWithOptions( publisherSelectEl, Publisher.instances, "name");
    // define event handler for submitButton click events    
    submitButton.addEventListener("click", this.handleSubmitButtonClickEvent);
    // define event handler for neutralizing the submit event
    formEl.addEventListener( 'submit', function (e) { 
      e.preventDefault();
      formEl.reset();
    });
    // replace the manageBooks form with the Book-C form
    document.getElementById("Book-M").style.display = "none";
    document.getElementById("Book-C").style.display = "block";
    formEl.reset();
  },
  handleSubmitButtonClickEvent: function () {
    ...
  }
};

When the user clicks the submit 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:

  handleSubmitButtonClickEvent: function () {
    var formEl = document.querySelector("section#Book-C > form");
    var slots = {
        isbn: formEl.isbn.value, 
        title: formEl.title.value,
        year: formEl.year.value,
        publisherIdRef: formEl.selectPublisher.value
    };
    // check input fields and show constraint violation error messages 
    formEl.isbn.setCustomValidity( Book.checkIsbnAsId( slots.isbn).message);
    /* ... (do the same with title and year) */
    // save the input data only if all of the form fields are valid
    if (formEl.checkValidity()) {
      Book.add( slots);
    }
  }

The setupUserInterface code for the update book use case is similar.