Like for the Create use case, a controller action method
is defined in the BookController
class:
public class BookController {
...
public String update( String isbn, String title, int year) {
try {
Book.update( em, ut, isbn, title, year);
} catch ( Exception e) {
e.printStackTrace();
}
return "update";
}
...
}The Book.update takes care of saving property value changes for a book object
identified by its isbn value as shown below:
public class Book {
...
public static void update( EntityManager em, UserTransaction ut,
String isbn, String title, int year) throws Exception {
ut.begin();
Book book = em.find( Book.class, isbn);
if (title != null && !title.equals( book.title)) {
book.setTitle( title);
}
if (year != book.year) {
book.setYear( year);
}
ut.commit();
}
...
}Now, we create the view where a Book can be selected so the user can edit the
title and year properties, and then save the changes. The code for
this view is stored in the WebContent/views/books/update.xhtml file which has the
following
content:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="..."
xmlns:h="..." xmlns:f="..." xmlns:p="...">
<ui:composition template="/WEB-INF/templates/page.xhtml">
<ui:define name="main">
<h:form id="updateBookForm">
<h:panelGrid columns="2">
<h:outputLabel for="selectBook" value="Select book: " />
<h:selectOneMenu id="selectBook" value="#{book.isbn}">
<f:selectItem itemValue="" itemLabel="---" />
<f:selectItems value="#{bookCtrl.books}" var="b"
itemValue="#{b.isbn}" itemLabel="#{b.title}" />
<f:ajax listener="#{bookCtrl.refreshObject( book)}"
render="isbn title year"/>
</h:selectOneMenu>
<h:outputLabel for="isbn" value="ISBN: " />
<h:outputText id="isbn" value="#{book.isbn}" />
<h:outputLabel for="title" value="Title: " />
<h:inputText id="title" value="#{book.title}" />
<h:outputLabel for="year" value="Year: " />
<h:inputText id="year" p:type="number" value="#{book.year}" />
</h:panelGrid>
<h:commandButton value="Update"
action="#{bookCtrl.update( book.isbn, book.title, book.year)}"/>
</h:form>
<h:button value="Main menu" outcome="index" />
</ui:define>
</ui:composition>
</html>In this facelet template, a single selection list (that is, a single-select HTML
element) is created with the help of the JSF element h:selectOneMenu, where the
selection list items (the HTML option elements) are defined by the JSF elements
f:selectItem or f:selectItems. The value attribute of
h:selectOneMenu binds book.isbn to the value of the selected item (or
option element). The selection list is populated with book records with the help of
a f:selectItems element bound to bookCtrl.books. The attributes
itemLabel and itemValue define the option elements' text
and value.
In the update view, when the user selects a book from the selection list, the form fields
are filled with the (ISBN, title and year) property values of the selected book. While the ISBN
is immediately available in the view (on the front-end) as the value of the selected
option element, the values of the title and year
properties have to be fetched from the back-end database. This can be done with the help of the
JSFelement f:ajax, which performs an XHR request for invoking a remote method,
bookCtrl.refreshObject, on the back-end. This method takes the managed
book bean, and updates its title and year properties with
the current values retrieved from the database. Its code is the
following:
public class BookController {
...
public void refreshObject( Book book) {
Book foundBook = Book.retrieve( em, book.getIsbn());
book.setTitle( foundBook.getTitle());
book.setYear( foundBook.getYear());
}
...
}To enforce a refresh of the form after the user's selection, such that it displays the
values of isbn, title and year, the f:ajax
element allows specifying form fields to be updated with the render attribute like,
in our case, render="isbn title year".
Finally, the h:commandButton element is used for invoking the
update action method of the BookController with the parameters
isbn, title and year, for making the changes
persistent.