The (AJA)X Files – On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience

Any article discussing AJAX is currently an instant hit. Articles talking about JSF (Java Server Faces) are also pretty hot. So the combination must drive the number of reads through the roof… While many developers are trying to figure out how the AJAX and JSF paradigms can be sensibly merged together, one of the richest JSF implementations on the market – ADF Faces, created by Oracle and donated to the Open Source Apache MyFaces project – is equipped with a lot of on-board AJAX capabilities. In this article I will show you which AJAX tricks ADF Faces has up its sleeves and how we can use them – virtually without any programming.

We will see a Box Office application where we can order Theater Tickets. It has a particularly responsive interface that has the following features

  • instant re-calculation of the over-all price of the tickets
  • refresh of the list of available shows when I change my type-of-show preferences
  • instant validation of the number of tickets I order (as this number is limited for popular shows)
  • dynamic enable/disable of the seating item – some shows do not have a seating arrangement
  • adopt the thumbnail image shown to the show selected

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience 

I believe that this list contains the most valuable uses of the concept of AJAX – which I would define as ‘having the web client communicate with the server without the user being aware and possibly update specific sections of the page based on the response received from the server, all to make for a quicker response to a user’s actions’. ADF Faces does not use the XmlHttpRequest Object, usually associated with AJAX. Does that make this any less AJAX? Not in my book.

Instant calculation of derived values, validation of newly entered values, updates of selection lists and refresh of screen widgets (enable/disable or hide/show) is I believe the bulk of what AJAX can do for data entry driven web applications.

....
 

Introducing the Box Office application

Let’s talk for a brief moment about what our application will do. The user can select a show, a number of tickets and a seating arrangement (when available). The application will instantly re-calculate the price of the order, whenever either the show, the number of tickets or the seating arrangement is changed. When the user picks a show without seating arrangement, the seating arrangement radiogroup is immediately disabled.

To help with the selection of the show, there are Categories listed. Using checkboxes, the user can specify which types of shows he is interested in. The list of shows is automatically refreshed whenever a checkbox is checked or unchecked. 

For the show the user selects, a thumbnail image is displayed (when available).

Finally, the user can specify the date on which he and his party would like to attend. The Date Chooser is updated immediately with the Play List for the selected show and any date entered is subject to immediate validation against the schedule of the show.

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience

The picture above indicates which parts of this user interface dynamically (AJAX-style) respond to user actions.

Before we go into how each of these dynamic actions was implemented in ADF Faces, let’s first inspect the application in general.

The Box Office Application

The application is created using Oracle JDeveloper 10.1.3. There is no backing database. It application uses a single JSPX file – more on that later – and three classes: Show – the Model bean that represents a theater production with properties like title, the opening and closing date, the category, the base ticket price and a (reference to a) thumbnail image. The BookingBean is the backing bean for the UI components’ value-properties, used by the application to hold the values entered by the user. It also has the logic for calculating the total price. The BoxOfficeBean is something like a Controller in this application. It instantiates a collection of shows, performs validations (well, actually there is just the validation of the number of tickets) and it manipulates the contents of the List of Shows whenever the Categories multi-checkbox item is modified.

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience

 

 The JSF page in our application – BoxOffice.jspx – imports the tag-libraries for the default (RI) Core and HTML JSF tag-libraries as well as the Core and HTML ADF Faces tag-libraries:

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:afh="http://xmlns.oracle.com/adf/faces/html"
xmlns:af="http://xmlns.oracle.com/adf/faces">
 

The page was created in JDeveloper from the New Gallery, selecting the tag-libraries in the wizard. No coding was required.

Using the Component Palette, the page was created by simply dragging and dropping the relevant components. The main layout-structure was created using the Panel Page with facets for Branding, Menu (not used), Title, Copyright etc. The logo was added to the page by simply dragging the logoamis.gif file onto the branding facet in our Panel Page!

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience

An ordinary Panel Grid was used to create the overall structure with two columns: one for the field prompts and the other for the fields themselves. The page in design time – the ADF Faces WYSIWYG editor in JDeveloper – looks like this:

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience 

Note that we can switch to source view at any time and directly type the XML, rather than use drag & drop, wizards and property editors. If we feel we have to.

Implementing AJAX Features with ADF Faces

I promised you a discussion of four major AJAX-style bevhaviors and their implementation in ADF Faces. So here we go with Calculation, Validation, List Refresh, Component Manipulation (like disabling/enabling).

Calculation of Total Price

The total price of the fictional Box Office order is refreshed whenever the user changes either the Show, the Seating or the Number of Tickets.  The total price UI Component is defined like this:

<af:panelLabelAndMessage label="Total Price"
inlineStyle="font-weight:bold;">
<af:outputText value="#{bookingBean.totalPrice}"
partialTriggers="f1:pc f1:show f1:seating "/>
</af:panelLabelAndMessage>
 

We can see that its value is bound to the bookingBean’s totalPrice property. So that is where it gets its value from. That by itself does not get us any AJAX-style, immediate refresh functionality. We need to additional things for that. The first is also in the snippet above: the partialTriggers property. We have specified that we want ADF Faces to refresh – client side, DHTML based refresh – whenever the three components specified with f1:pc, f1:show and f1:seating have changed. These three, as will not be surprise, are respectively the Number of Tickets, the Show and the Seating Arrangement input items.

The final piece in the puzzle is found with these three components. For each of them, we have set the autoSubmit property to true. See
here the examle of the Number of Tickets element:

<af:outputLabel id="partyLabel" for="pc" value="Number of Tickets"/>
<af:inputText value="#{bookingBean.partyCount}"
autoSubmit="true"
id="pc"
validator="#{boxOfficeBean.validatePartyCount}">
 

This tells the ADF Faces framework that whenever the value of this inputText is changed by the user – onChange JavaScript event fires – there should be an Asynchronous post of all values in the current input-form. The ADF Faces request life-cycle is performed and the results are sent back to the client. This is all done using a – hidden – IFRAME. ADF Faces contains JavaScript libraries that can interpret the contents received in the IFRAME and process them into the page as partial page updates using DHTML (JavaScript DOM manipulation).

This means: if user changes either the selected Show, the Seating or the Number of Tickets, an AJAX-like request is sent to the server, all form values are applied, validation is performed and all attributes bound to backing bean properties are recalculated and a fresh (partial) page is rendered and sent back to the server. For the Total Price this means that the bookingBean.getTotalPrice() method is called:

public double getTotalPrice() {
double seatingFactor = 1;
if (seating !=null) {
if (seating.equalsIgnoreCase("orch"))
seatingFactor = 2.0;
if (seating.equalsIgnoreCase("mez"))
seatingFactor = 1.3;
if (seating.equalsIgnoreCase("sro"))
seatingFactor = 0.5;
}
return (show==null?0:show.getBaseTicketPrice()) * partyCount * seatingFactor;
}
 

When the partial page update is performed client side, the total price is refreshed from whatever value was produced by the getTotalPrice() method.

All in all the functionality is very much like coding an onChange JavaScript event trigger that performs the above logic. However, that JavaScript function would need to have knowledge of the base ticket price for all Shows in the entire show collection. In addition, accessing the various form fields client side is not as simple as simply accessing bean properties. To cut a long story short, all we had to do to implement this dynamic recalculation of the Total Price was setting three autoSubmit properties to true, setting the partialTriggers attribute on the totalPrice item and coding the getTotalPrice() method – which we needed anyway.

Synchronize the Thumbnail Image

Well, they do not get much easier than the thumbnails. Each Show has a property thumbnailUrl that is initialized with the URL where a fitting image for the show can be found. The image is included in the page using the objectImage element. We bind its source property to the thumbnailUrl property in the show currently selected in the bookingBean:

<af:objectImage id="showThumbnail" source="#{bookingBean.show.thumbnailUrl}"
height="150" width="100"
partialTriggers="f1:show"/>

As you might have guessed by now, the partialTriggers property specifies that this objectImage should be partially refreshed whenever the f1:show element is changed (so whenever a new show is selected). We have seen already with the total price calculation that we also need the show element to set its autoSubmit property to true. To ensure that an asynchronous , background request is sent with the newly selected show among the values submitted.

As a result, selecting a new show in the list will immediately refresh the thumbnail image:

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adffacesThumbnail1 

and after selecting The Woman in Black (just clicking on it):

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adffacesThumbnail2

Instant Validation of Number of Tickets and Date of Performance

Validation is obviously a core feature of Java Server Faces. Basic validation is available from a small set of standard validators. ADF Faces extends this set with some additional validators – like validateDateTimeRange validator and Regular Expression validator – and enhances the validators to also perform Client Side validation. This means that without communication with the server a value is validated in JavaScript code. When a validation error is encountered, an appropriate alert is shown.

Let’s start simple. The Box Office does never allow more than 8 tickets to be ordered (to discourage the black market). We have specified so in the page definition:

<af:inputText value="#{bookingBean.partyCount}" autoSubmit="true"
id="pc"
validator="#{boxOfficeBean.validatePartyCount}">
<f:validateLongRange maximum="8" minimum="1"/>
</af:inputText>

If the user asks for more than 8 tickets, as soon as he tries to leave the field validation is done and the standard ADF Faces message is shown:  

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adffacesClientVal

 

However, our validation requirements are a little more subtle than this. Each show can have its own maximum number of tickets that can be acquired in a single transaction. So in addition to the static client side validation you see here, we have set up a validator – the method validatePartyCount in the boxOfficeBean. This method is shown here:

public void validatePartyCount(FacesContext facesContext, 
UIComponent uiComponent, Object object) {
Object submittedShow = getSelectOneShowList().getValue();
if (submittedShow!=null) {
Short maxParty = ((Show)submittedShow).getMaxParty();
if ((Integer)object> maxParty ) {
((CoreInputText)uiComponent).setValid(false);
FacesMessage message = new FacesMessage("The maximum of tickets for this show is unfortunately limited to "+maxParty+"!");
facesContext.addMessage(uiComponent.getClientId(facesContext), message);
}
}
}
 

You can tell that this validator method inspects the value of the Number of Tickets element – (Integer)object – and compares it to the maximum number of tickets specified for this show – ((Show)getSelectOneShowList().getValue()).getMaxParty(). Note that we make use of the getSelectOneShowList method that holds the SelectOneList element that was bound to it from the binding property:

<af:selectOneListbox tip="Select the show of your choice!"
... binding="#{boxOfficeBean.selectOneShowList}">
 

If the number of tickets desired by the user exceeds the maximum number allowed for this particular show, a new message is added to the FacesContext.

In order to ensure that this message is actually presented to the end-user as part of the dynamic page refresh, we have to set the partialTriggers property for the messages component. This tells ADF Faces to refresh the messages container when a partial page refresh is performed as a result of changes in either the show, the pc (party count or Number of Tickets) or showDate element:

<af:messages id="msg" partialTriggers="f1:show f1:pc f1:showDate "/>
 

If we change either the Show when we have already set a too high number of tickets or change the number of tickets to a higher number than allowed for the currently selected show, the Validator will kick in and present us with an error message:

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adffacesNumberOfTicketsValidation 

Note: we could also have made the client side validation a little more dynamic by binding the maximum property of the validateLongRange validator:

<f:validateLongRange maximum="#{boxOfficeBean.show.getMaxParty}" minimum="1"/>

However, this does not give us the opportunity to create tailored error messages. Which we did not really do in this example either… 

We have one final piece of validation up our sleeves: date validation. Each one of the shows presented in this application has a specific opening and closing date. That means two things for the application

  • the date chooser element – the little calendar shown behind the Date of Performance element – should only present days in the valid range for the currently selected show
  • the dates entered by the user directly into the Date of Performance element must be validated to be in the period during which the show is running

The first feat is accomplished very easily by binding the maxValue and minValue attributes of the chooseDate element to the opening and closing date properties of the currently selected show, as available from thye bookingBean:

<af:chooseDate id="chooseDate1"
maxValue="#{bookingBean.show.closing}"
partialTriggers="f1:show"
minValue="#{bookingBean.show.opening}"/>

To make sure that the dateChooser is refreshed whenever a new show is selected, we have set the partialTriggers property to tie in with the f1:show element. The result:

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adfFacesDate1

Change the selected show and watch what happens to the Date Chooser:

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adfFacesDate2 

The second requirement – validating the date entered by the user – is even easier. We can use the standard ADF Faces validateDateTimeRange validator:

<af:selectInputDate id="showDate" chooseId="chooseDate1"
value="#{bookingBean.showDate}"
partialTriggers="f1:show " autoSubmit="true"
tip="Specify the date on which you would like to attend the show">
<af:convertDateTime/>
<af:validateDateTimeRange maximumMessageDetail="This show does not run after {2}"
maximum="#{bookingBean.show.closing}"
minimum="#{bookingBean.show.opening}"
minimumMessageDetail="This show does not open until {2}"/>
 ...

The validation is performed whenever we change the show selection or the value for Date of Performance. However, I do not always yet get the pretty error messages I feel I deserve having followed the ADF Faces guidelines. What I get is the following:

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adfFacesDate3 

The detailed pretty message is only displayed after changing the show, not after entering a new date value. I may have set the partialTriggers property at the wrong level… Perhaps I should only allow the users to set the Date of Performance through the Date Chooser; then they could never run into this validation failure.

List Refresh

A frequent example in discussions of AJAX is the dynamic adjustment of a list of allowable values. In our Box Office application, we have a SelectManyCheckbox element that display four categories of Theater Experiences: Play, Musical, Ballet and Opera. The list of shows we present to the user must contain only those shows that correspond to the categories currently selected by the user. So the SelectOneList element that displays the Shows must be updated whenever the selection of categories change.

This is much easier than it may seem to you. Let’s first look at the UI Components in our page definition:

<af:outputLabel for="cat" value="Category of Shows"/>
<af:selectManyCheckbox value="#{bookingBean.categories}"
tip="The category of shows"
layout="vertical" autoSubmit="true"
id="cat">
<af:selectItem label="Play" value="pla"/>
<af:selectItem label="Musical" value="mus"/>
<af:selectItem label="Ballet" value="bal"/>
<af:selectItem label="Opera" value="ope"/>
</af:selectManyCheckbox>
<af:outputLabel id="showLabel" for="pgShow" value="Show"/>
<af:panelGroup id="pgShow" partialTriggers="f1:show"
layout="horizontal">
<af:selectOneListbox tip="Select the show of your choice!"
size="5" autoSubmit="true"
value="#{bookingBean.show}" id="show"
partialTriggers="f1:cat"
binding="#{boxOfficeBean.selectOneShowList}">
<f:selectItems value="#{boxOfficeBean.showSelectItems}"/>
</af:selectOneListbox>
 

We can tell from autoSubmit="true" on the cat element that changing the selection of Categories will initiate a partial asynchronous request cycle. The partialTriggers property on the show element includes f1:cat; this specifies that the list of Shows is to be refresh whenever a Partial Page refresh is performed as a result of a change in Category Selection.

The selectItems element that provides the values displayed in the Show-list is bound to the getShowSelectItems() method on the boxOfficeBean. So clearly that particular method should select and return the shows that satisfy the category condition.

public SelectItem[] getShowSelectItems() {
String lastCategory="";
SelectItem[] items = new SelectItem[ shows.size()+10];
int i=0;
for (Show show:this.shows) {
if (fitsCategory(show.getCategory(), this.bookingBean.getCategories()))
items[i++] = new SelectItem(show,show.getTitle());
}
return items;
}

private boolean fitsCategory(String cat, String[] cats) {
if (cats!=null) {
Collection<String> labels = Arrays.asList(cats);
return labels.contains(cat);
}
else return false;
}

The getShowSelectItems() method prepares a SelectItem[] array, iterates through the entire set of shows available in the BoxOfficeBean from the shows member variable and has the method fitsCategory determine, based on the category of the show compared to the set of categories currently selected by the user, whether or not to include the show in the set that is returned from the method. See the example below:

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adffacesCats1

Now if we add Ballet and Opera to our selection, the Show list is refreshed instantly:

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adffacesCats2

Component Manipulation – Enable/Disable the Seating Arrangement

Not all shows have a seating arrangement. Most Operas do while most plays and musicals do not. Our user interface should respond accordingly: if the user p
icks a show withou
t a seating arrangement, we will disable the Seating Arrangement element. If the show does have one, we will enable the element. Note: we could also make it disappear or appear. We chose not to as it makes for a less consistent, less appealing experience. There is no technical limitation.

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adffacesSeating1 

Implementing this dynamic switching on and off is – as you are used to by now – amazingly simple and largely declarative. The disabled property of the selectOneRadio element is bound to the hasSeatingArrangement method in the Show bean returned by the gertShow() method in the bookingBean.

<af:selectOneRadio value="#{bookingBean.seating}"
autoSubmit="true" id="seating"
partialTriggers="f1:show "
disabled="#{!bookingBean.show.hasSeatingArrangement}">
<af:selectItem label="Orchestra" value="orch"/>
<af:selectItem label="Mezzanine" value="mez"/>
<af:selectItem label="Balcony" value="bal"/>
<af:selectItem label="Standing Room" value="sro"/>
</af:selectOneRadio>
 

To make sure the disabled property is re-evaluated during partial page refresh, we have to set the partialTriggers property of the Seating Arrangement element to include the f1:show element. This means: if another show is selected, refresh the Seating Arrangement element.

The (AJA)X Files - On the built-in AJAX facilities of ADF Faces for zero-code Rich User Experience adffacesSeating2

Resources

Download the JDeveloper 10.1.3 Project with our three beans and the Box Office jspx page:  BoxOffice.zip. Simply download, extract and open the TicketService.jpr file in your JDeveloper 10.1.3 instance. Note1: you may have to copy adf-faces-impl.jar to the public_html\WEB-INF\lib directory. You can find this file in your JDeveloper 10.1.3 installation: JDEV_HOME\jlib . Note2: you should be able to run the application in other IDEs as well, as long as you have access to the ADF Faces libraries.

14 Comments

  1. Sergei October 25, 2007
  2. IT March 7, 2007
  3. IT March 7, 2007
  4. Alberto Morales February 1, 2007
  5. Eron Yang September 5, 2006
  6. site admin June 28, 2006
  7. Geetha June 28, 2006
  8. Lucas Jellema June 21, 2006
  9. bill June 20, 2006
  10. Lucas Jellema June 20, 2006
  11. Sagar Malisetti June 20, 2006
  12. Lucas Jellema May 27, 2006
  13. Lucas Jellema May 26, 2006
  14. Didier Laurent May 25, 2006