5. Write the User Interface Code

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

For showing information about the authors of a book in the list books use case, the corresponding cell in the HTML table has to be filled with a list of the names of all authors. For this, we implement a method in the Book class which returns the serialized (as we like to have it displayed) list of author names (in the form, authorName_1, authorName_2, ..., authorName_n):

public String getAuthorNames() {
  String result = "";
  int i = 0, n = 0;
  if ( this.authors != null) {
    n = this.authors.size();
    for ( Author author : this.authors) {
      result += author.getName();
      if ( i< n - 1) {
        result +=", ";
      }
      i++;
    }
  }
  return result;
 }

In the view file, WebContent/views/books/listAll.xhtml we use this method in the JSF expression corresponding to the authors cell:

<ui:composition template="/WEB-INF/templates/page.xhtml">
  <ui:define name="content">
    <h:dataTable value="#{bookController.books}" var="b">
      ...
      <h:column>
        <f:facet name="header">Publisher</f:facet>
        #{b.publisher.name}
      </h:column>
      <h:column>
        <f:facet name="header">Authors</f:facet>
        #{b.authorNames}
      </h:column>
    </h:dataTable>
    <h:button value="Back" outcome="index" />
  </ui:define>
</ui:composition>

Remember, using b.authorNames results in calling a method named getAuthorNames of the specific b object.

5.2. Allow selecting associated objects in the create use case

For allowing to select multiple authors to be associated with the currently edited book in the create book use case, a multiple selection list (a select element with multiple="multiple"), as shown in the HTML code below, is populated with the instances of the associated object type. The following code is part of WebContent/views/books/create.xhtml view file:

<ui:composition template="/WEB-INF/templates/page.xhtml">
  <ui:define name="content">
    <h:form id="createBookForm">
      <h:panelGrid columns="3">
        ...
        <h:outputLabel for="authors" value="Authors:" />
        <h:selectManyListbox id="authors" value="#{book.authors}">
          <f:selectItems value="#{authorController.authors}" var="a" 
                         itemLabel="#{a.name}" itemValue="#{a}" />
          <f:converter converterId="AuthorConverter" />
        </h:selectManyListbox>
        <h:message for="authors" errorClass="error" />
      </h:panelGrid>
      <h:commandButton value="Create" action="#{bookController.add( 
       book.isbn, book.title, book.year, book.publisher, book.authors)}"/>
    </h:form>
    <h:button value="Back" outcome="index" />
  </ui:define>
</ui:composition>

Remember that we have to add the equals method for the Author model class. In our case, two authors are "one and the same" if the values of their personId property are equal:

@Override
public boolean equals( Object obj) {
  if (obj instanceof Author) {
    Author author = (Author) obj;
    return ( this.personId.equals( author.personId));
  } else return false;
}

The same like in the case of the Publisher, a JSF converter class is used to serialize authors from object instance to view strings, when the select list has to be populated, and back to object instance, when the add method of the BookController is called (the "Create" button was pushed by the user). The BookController.add method is updated with a new parameter, i.e., the corresponding list of selected authors:

public String add( String isbn, String title, Integer year, Publisher publisher, Set<Author> authors) {
  try {
    Book.add( em, ut, isbn, title, year, publisher, authors);
    // Enforce clearing the form after creating the Book row.
    // Without this, the form will show the latest completed data
    FacesContext facesContext = FacesContext.getCurrentInstance();
    facesContext.getExternalContext().getRequestMap().remove( "book");
  } catch ( EntityExistsException e) {
    try {
      ut.rollback();
    } catch ( Exception e1) {
      e1.printStackTrace();
    }
    e.printStackTrace();
  } catch ( Exception e) {
    e.printStackTrace();
  }
  return "create";
}

The update book use case is very similar with the create book use case.