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.