Ajax and Partial-Page Refresh in Oracle ADF Rich Client


Get an overview of the support for Ajax and PPR in the Oracle Application Development Framework (Oracle ADF) Faces rich client (RC) constituent of Oracle ADF 11g

By Lucas Jellema Oracle ACE Director and Chris Muir Oracle ACE Director

Published July 2009

Today's Web 2.0 applications owe an important part of their rich behavior to their asynchronous communication with the server and their dynamic partial-page updates. While users are working their way around a page, the application maintains all kinds of background interactions. The results of these interactions are used for the dynamic behavior of the application, such as updating field values; presenting users with instant validation feedback; filtering lists with selectable values; and updating the page with details of recent events, such as changes in the database, reception of chat messages, or new entries in RSS feeds of interest. The net effect of Ajax (Asynchronous JavaScript and XML) and partial-page refresh (PPR) is a responsive, dynamic, interactive, alive application that is both attractive and effective.

In this two-part series, Oracle ACE directors Chris Muir, in Australia, and Lucas Jellema, in The Netherlands, have joined forces to provide an overview of the support for Ajax and PPR in the Oracle Application Development Framework (Oracle ADF) Faces Rich Client (RC) constituent of Oracle ADF 11g. They provide a step-by-step introduction to both the basics and the more advanced aspects of Ajax and PPR in Oracle ADF Faces RC. The two articles include many code examples as well as an Oracle JDeveloper application with the complete source code. This first article discusses the functionality that is available through simple declarative means, and the second takes us to a more advanced level of client/server integration using programmatic facilities including the Oracle ADF Faces RC client-side (JavaScript) API.

Prerequisites and Preparation

This article pertains to Oracle ADF 11g and Oracle JDeveloper 11 g, the production release currently available for download on Oracle Technology Network.

The examples discussed are all available in an Oracle JDeveloper 11g application that can be downloaded here. Note that no database is required for running this application.

The S.H.O.P. Case

The case that provides the context for the examples presented in this article is the Second Hand Opportunities Plaza (or S.H.O.P. for short), an eBay-like Web community site where people can do their online garage sales—posting advertisements for items they want to part with and hope to find a new owner for—preferably making some money in the bargain (see Figure 1).

Figure 1 S.H.O.P. logo

Introduction to Asynchronous Interaction and Partial-Page Refresh

Modern, rich Web 2.0 applications should not use a “full-page submit and reload” approach. We want pages to be partially, dynamically refreshed and validations to take place instantaneously when we leave the field, not only when we submit the entire page. Derived, calculated fields should be refreshed not only when the full page is submitted but also as soon as possible. When we make certain selections on a page, dependent lists of values should be updated immediately (such as selecting a country and immediately seeing  a list of provinces or cities). When an event such as a change in the database, receipt of a chat message, or a new entry in an RSS feed occurs on the server, we want the client to present relevant information to the user as quickly as possible, even if the user doesn't explicitly ask for it.

To generalize: the application should react quickly to user actions without the user's having to explicitly ask for a reaction (by, say, clicking a Submit button) and perhaps even without the user's knowing about it (no telltale busy cursor, for example). And the reaction—whether it is a fresh value for a calculated field, a message about validation failure, or the display of appropriate context information based on a certain selection—should not lead to a full-page refresh but, rather, should be applied in a subtle way through a refresh of the relevant section of the page.

Asynchronous interaction is nonblocking application-initiated activity and background communication between browser and server that is (largely) invisible to the end user. Partial-page refresh uses the outcome of the asynchronous communication to repaint the page in designated areas. PPR works with the browser's Document Object Model (DOM), through JavaScript, sometimes referred to as dynamic HTML (DHTML).

One of the downsides of Ajax and PPR in recent years has been the development challenge. Apart from often-complicated and hard-to-automate testing, it has usually been a messy affair to implement the rich functionality. The asynchronous programming paradigm itself is complex, as is the DOM manipulation required for PPR. JavaScript programming lends itself to a somewhat unstructured and not very productive development approach that can lead to code that is hard to debug and maintain, and it requires skills that Java Web application developers don't typically have. Many Web applications have not exploited rich Web 2.0 options to the fullest.

With Oracle ADF Faces RC, implementing Ajax and PPR functionality has largely been raised to the level of declarative configuration and server-side programming, which means that it is an integral part of developing Web applications with Oracle ADF Faces RC components. It takes surprisingly little effort to achieve dynamic, instantaneous responses to user browser activity, thanks to the built-in mechanisms of Oracle ADF.

Within Oracle ADF, behind the scenes, an asynchronous interaction called a PPR request is started in JavaScript. The request is posted to the server, where it goes through a specialized version of the Oracle ADF (JavaServer Faces [JSF]) lifecycle in which only the required components render for the response, in a specially wrapped way. The response is processed in JavaScript, and its contents are used to refresh the designated target areas on the page. Although the PPR request/response cycle itself is invisible to the user—it takes place in the background on the server, which does not freeze the browser and has no impact on the actions the user can perform on the page—processing of the PPR response on the browser can, of course, have an effect on the page. The partial-page refresh that may occur takes the HTML rendered by the server-side application in the PPR response and uses it to refresh the client-side manifestation of the components involved—this involves manipulation of the browser DOM through the component-based client-side API in Oracle ADF Faces RC.

Built-in PPR Functionality

The main purpose of this article is to explain how you can leverage the PPR functionality of Oracle ADF Faces RC for your own custom applications. It is important to realize that many Oracle ADF Faces components have partial-refresh functionality implemented natively. Simply by using these components, you build on top of the PPR infrastructure. For example, the Oracle ADF Faces rich table, rich tree, and rich tree table components as well as all data visualization components use PPR to postload the data they present. After the page has first loaded—these components do not yet contain any data—the onload event for the page triggers an automatic PPR request to fetch the corresponding data from the server.

For tables or other components, you may have seen a “Fetching Data…” message, an indication that the PPR order to fetch the data is in progress, as in Figure 2. This happens with tables that have their ContentDelivery attribute set to lazy.

Figure 2 Asynchronous Data fetching in progress

As the user scrolls through the table, more data is fetched from the server—by additional PPR requests. A similar example is the rich tree with PPR-based node expand and collapse. The Ajax characteristics demonstrated by these features are initiated by the user's activating (clicking) a control element and the application's responding with a local refresh of a portion of the page—with no full-page refresh, blank page with hourglass, or browser lock. When you sort the records in the table as in Figure 3, for example, only the contents of the table are refreshed and nothing else on the page is repainted.

Figure 3 Some of the built in Partial Page refresh features of the rich table: disclose detail, filter and sort rows

Note that in addition to the built-in Oracle ADF Faces RC Ajax capabilities that require client/server communication in the background, there is a lot of dynamic behavior in the rich client components that is handled completely on the client side unless the developer explicitly requests notification to be sent to the server. Examples are client-side validation, table column resizing, switching between tabs and sections, tabbed panel and accordion, drag-and-drop, and showing and hiding pop-ups.

First Steps with Oracle ADF Faces RC Partial-Page Refresh: Instantly Submitting Changed Field Values with autoSubmit

The first concept to learn about PPR in Oracle ADF Faces RC is the use of the autoSubmit attribute on any input component. This attribute, when set to true, instructs the Oracle ADF framework to automatically submit any user change in the value of the component to the server. When the user navigates out of such an autoSubmit item whose value has been changed, the form is submitted in an asynchronous PPR request and normal processing—conversion, validation, model update, rerender—will take place for the component. At the end of the PPR cycle, the server is synchronized with the client, at least for this one component.

In the following example, we have a page for posting a new-product advertisement. The user can set the product's name, year of original purchase, price, and description. The inputText component for the price has the autoSubmit attribute set to true. This means that when the user changes the price and navigates out of the price item, the form will be asynchronously submitted and the price component will be processed through the Oracle ADF Faces lifecycle.

<af:inputText id="productpriceIT" label="Price" autoSubmit="true" 
              value="#{advertisement.price}" >


The setPrice() method in the advertisement bean writes the newly set value to the console.
public void setPrice(float price) {
System.out.println("New value for product price = "+price);
this.price = price;
}

We can see the new value entered in the field appearing on the console as soon as we navigate out of the Price field (see Figure 4). This demonstrates how a change in price triggers a PPR request that processes the value, including an update of the model.

Figure 4 When a new value is entered in the client, it is sent to the server and it appears on the console

Note that when you change the name of the product (or any other item), no PPR request is triggered and that when you change the price after changing the name, the following PPR request processes not the name but only the autoSubmitted price component.

Suppose we want to enforce that the price is between $5 and $500 and is an integer for amounts of more than $50. We can set the validator attribute on the price inputText to call a method that validates the value entered.

<af:inputText id="productpriceIT"                     label="Price" autoSubmit="true"  
validator="#{advertisement.checkPrice}" value="#{advertisement.price}" >

The validator method—checkPrice()—is implemented in the advertisement bean; it inspects the new value against the two business rules. Upon violation of a rule, an exception is raised. This both cuts short the JSF lifecycle and adds an error message to FacesContext:

public void checkPrice(FacesContext facesContext, UIComponent uIComponent,
Object value) {
System.out.println("CheckPrice invoked - new price value = "+value);
float price = (Float)value;
if (price > 500) {
((EditableValueHolder)uIComponent).setValid(false);
throw new ValidatorException( new FacesMessage (FacesMessage.SEVERITY_ERROR, "Price should not be over $500", "S.H.O.P. has a policy of not allowing transactions for more than $500 as to retain a community spirit and prevent abuse"));
}
if (price > 50 && price - (int)price > 0) {
((EditableValueHolder)uIComponent).setValid(false);
throw new ValidatorException(new FacesMessage (FacesMessage.SEVERITY_ERROR, "Prices over $50 should not carry cents", "Cents are just a marketing trick"));
}
}

If we now run the page and change the value in the Price field to, say, $106.95 and leave the field, PPR will take place; check the price; and reject it, as indicated by the red outline around the Price field (see Figure 5).

Figure 5 Error message triggered in the server by price that violates business rule

The setPrice() method is not called in this case, because the PPR request lifecycle is cut short—when validation fails, no model update is performed.

Note: It would, of course, be easier to demonstrate the automatic validation step in the PPR request by using one of the declarative validators. However, Oracle ADF Faces RC provides client-side implementation of the declarative validators, so instead of relying on the validation in a PPR request, the framework instantly validates the field value by using the client-side validation facility. In Oracle ADF Faces RC, we cannot switch off the client-side validation—unlike in Oracle ADF Faces 10 g.

Alternatively, instead of troubling the user with an error message saying that the price's value doesn't match our business rule, we could simply have the bean make the necessary adjustments and issue a warning instead. In the following example, the setPrice() method calls the verifyAndCorrectPrice () method to make sure the price has an integer value for amounts higher than $50. With this solution, the previous checkPrice() method can be modified to ignore this rule.

public void setPrice(float price) {
       System.out.println("New value for product price = "+price);
       this.price = verifyAndCorrectPrice(price);      
    }

    private float verifyAndCorrectPrice( float price) {
        float newprice = price;
        if (price > 50 && (price - (int)price)>0) { // price over $ 50 and cents involved
            newprice = Math.round(price);
            FacesContext.getCurrentInstance().addMessage
                 ( FacesContext.getCurrentInstance().getViewRoot().findComponent("productpriceIT").getClientId(FacesContext.getCurrentInstance())
                 , new FacesMessage(FacesMessage.SEVERITY_WARN, "Prices over $50 are in straight dollars - no cents",
                 "S.H.O.P. regards the use of cents in prices over $50 as an undesirable marketing gimmick and only
                  allows rounded numbers; the price was corrected from "+ price+ " to " + newprice)
                 );
        }
        return newprice;
    }

Figure 6 shows what we'll see after running the page, entering a value of $106.95 for the price, and leaving the field:

Figure 6 Page with warning message produced by the server after asynchronous autosubmit of the new price

Now we have an orange box around the Price field and a warning message that tells the user that the price has been adjusted. It would be nice to also show the new, corrected value in the Price field, which would mean modifying the Price component in the client at the end of the PPR request. Let's see how we instruct Oracle ADF to update a client side component as part of the PPR cycle.

Introducing the PartialTriggers Attribute for Partially Refreshing Oracle ADF Pages

The autoSubmit attribute is used to initiate the PPR request. The change in a component's value triggers the framework to submit the form and process the changed value, taking it through conversion, validation, and model update. So far, it is all one-way: submitting the value to the server. However, we have already glimpsed PPR functionality when first the red and then the orange box was drawn around the Price field to indicate the validation error and the correction warning. So the framework performs a little bit of PPR without our even asking for it.

Asking for PPR on the price component itself is simple. We simply have to instruct the framework to refresh the price component whenever it performs a PPR request that is initiated by the price component's autosubmitting itself. Oracle ADF uses the partialTriggers attribute to configure a component for partial refresh. This attribute can contain the ID values for all components whose PPR requests should trigger a component update. In this case, we want Oracle ADF to do a PPR of the price component itself when the price is changed:
< af:inputText id="productpriceIT" label="Price" autoSubmit="true"                validator="#{advertisement.checkPrice}"                value="#{advertisement.price}"                partialTriggers="productpriceIT" /> 
                                                 

With this instruction in place, Oracle ADF will initiate a PPR request when the price is modified. When the PPR response is handled, the component with the ID productpriceIT itself is refreshed.

Figure 7 The page before and after navigation out of the price field

Figure 7 shows the page just before the user navigates out of the Price field and soon afterward—when PPR has struck to correct the price and issue a warning message.

The autoSubmit/partialTriggers combination not only works for the changed component itself but can also be applied across components, to make dependent components refresh when their dependent field has been changed. For example, the outputText age component, which shows how many years a product has been used, depends on the Year field. Whenever the Year (of purchase) field changes, the age component should be refreshed. With autoSubmit (for Year) and partialTriggers (for Age), that is simple enough.

< af:inputText id="yearIT" label="Year" autoSubmit="true"
       value="#{advertisement.year}"/>
< af:inputText id="ageIT" label="Age" readOnly="true" partialTriggers="yearIT"
      value="#{advertisement.age} years " / >

When the year is changed, the year component initiates the PPR request. The new value in Year is processed, and the model is updated. When the PPR response is received in the browser, all components that refer to the year component in their partialTriggers attribute are refreshed. In this example, that applies to the Age field, which derives its value from the year component.

The getAge() method is implemented in the advertisement bean as follows:

public Integer getAge() {                                         Short currentYear = Short.parseShort( new SimpleDateFormat("yyyy").format(new java.util.Date()));                         return this.getYear() == 0 ? null : currentYear - this.getYear();     }

We can run the page, enter a value for Year, and navigate out of the field. PPR kicks in; processes the Year value; and takes care of updating the age component, showing the age derived from the new Year value, as shown in Figure 8.

Figure 8 PPR Updating the age component after the number of years has been changed

It is interesting to see that not only is the refresh of the page a partial affair but also that the same applies to the response returned to the browser at the end of the PPR request cycle. Using tools such as Firebug (a plug-in for Firefox), Fiddler (a freeware tool for analyzing HTTP traffic), or indeed the HTTP analyzer in Oracle JDeveloper itself, we can inspect the PPR request and response messages sent to and from the browser. The PPR request is largely just a submission of the form. The PPR response is not the entire page simply rerendered by the server but, rather, a very lightweight message that contains only the essentials for this PPR request cycle, which, in this example, means only the new state of the age component (see Figure 9):

Figure 9 The contents of the PPR response message for the partial update of AGE

The contents of this ultrathin response are processed by Oracle ADF Faces RC in the client and are used to refresh selected areas and components in the page.

We have seen before how Oracle ADF Faces RC will process the component that started the PPR request only through the normal lifecycle steps (convert, validate, update model, [re]render component). Then we extended this with the rerender of the components that refer to the changed component in their partialTriggers attribute, as we saw age being refreshed when the year was changed. It actually goes a little beyond this: the components with a partialTriggers reference are not only rerendered but they, too, are taken through the entire lifecycle. This means that, for example, when the name component has a partialTriggers reference on the year component, as in the following example:

< af:inputText id="nameIT" label="Name"                                                                       partialTriggers="yearIT"                                                                value="#{advertisement.name}"/>

and the year is changed, the PPR lifecycle will process not only the new year value but also the new name value. If the server finds any fault  with the name value or performs an operation on the value, this will have an effect on the outcome of the PPR cycle.

For example, when we have implemented the setName() method to change the name value entered by the user to uppercase and the user first enters a name for the product, navigates out of the Name field (nothing happens), and changes the value in the Year field, the PPR cycle will not only derive the age from the year value but also update the name of the product and show the new all-uppercase value, as shown in Figure 10.

Figure 10 Deriving age from the year value  also leads to uppercasing the name because of the dependency from the Name field on the Year component

You should take care with required items and their partialTriggers dependencies. In the example we just discussed, suppose the name component is mandatory (required=”true”) and the user entered a year value—say 2001—prior to entering a value for name, the PPR request would end with a validation error (see Figure 11) for the Name field, because the PPR lifecycle (convert, validate, update model, [re]render component) is fired for both components:

Figure 11 Validation error on Name after changing Year, due to dependency

Using Instant Conversion for More End-User Convenience

One of many ways we can use PPR is for instant conversion. JavaServer Faces has the concept of converters, components we can associate with input elements to convert the value stored in the underlying model to a human-readable representation of that value in the browser, and vice versa. We can, for example, use a DateTime converter to format date values or use number converters to present numeric values in a specific pattern. If we associate a converter with an input component, set autoSubmit to true on the component, and add the component's ID to its own partialTriggers attribute, the converter will perform an instant conversion when the user changes the value.

We will now make use of this PPR-based instant conversion by configuring a shorthand converter on the description component. This converter will inspect the description and replace several frequently used abbreviations with their full-length equivalents. Among others, it will replace occurrences of “agan,” “sitb,” “dbolo,” “ywli,” and “huaa” with “as good as new,” “still in the box,” “driven by old lady only,” “you will love it,” and “hardly used at all.”

The ShortHandConverter class code is shown here:
public class ShortHandConverter  implements Converter {       Map shorthand = new HashMap(20);        public ShortHandConverter() {           shorthand.put("agan","as good as new");            shorthand.put("sitb","still in the box");            shorthand.put("dbolo","driven by old lady only");            shorthand.put("ywli","you will love it");            shorthand.put("huaa","hardly used at all");            shorthand.put("nrod","no reasonable offer declined"); [[Would it be OK to put all      the commas inside the close quotes, which is standard American (not British Commonwealth)     punctuation style?]]           // more shorthand notations        }                  public Object getAsObject(FacesContext facesContext,                                   UIComponent uiComponent, String string){          String description = string;         for (String abbr:(Set< String>)shorthand.keySet()) {               description = description.replaceAll(abbr, (String)shorthand.get(abbr));          }              return description;        }        public String getAsString(FacesContext facesContext,                                             UIComponent uiComponent, Object object) {                                                                          return object.toString();       }     }

To make the converter available in the Oracle ADF Faces RC application, it needs to be configured in the faces-config.xml file, by use of an entry such as this:

 < converter>    < converter-id >ShortHandConverter< /converter-id>      < converter-class >otn.shop.converters.ShortHandConverter < /converter-class>    < /converter >
Now we can use the converter on the description component, which we also configure to perform autoSubmit and “auto PPR”:
< af:inputText id="descIT" label="Description" rows="8"                 autoSubmit="true" partialTriggers="descIT"                 value="#{advertisement.description}">       < f:converter converterId="ShortHandConverter"/> < /af:inputText>

Observe how autoSubmit=”true” makes the component send its modified value to the server for processing—which starts with conversion by ShortHandConverter—and how partialTriggers=”descIT” instructs Oracle ADF  to refresh the Description field at the end of the PPR cycle.

When we run the page with the converter configured, we can enter the product description for the advertisement in a somewhat more cryptic format that is immediately converted into a more readable format when we navigate out of the Description field (see Figure 12).

Figure 12 Converting a product description with shorthandconverter

To see the functionality described so far in action, run the page ProductAdvertisment_step1.jspx in the JDeveloper application provided with this article.

Creating a Cascading List of Values

We frequently use various types of list components in Oracle ADF Faces RC applications: drop-down (selectOneChoice), radio group (selectOneRadio) or list box, and shuttle or check box for multiple selections. In all these components, we present the user with several allowable options, values that can be selected rather than entered in free format.

On many occasions, the set of allowable values depends on user choices. When the application has a list of countries, its set of available countries can be restricted by selection of a continent or region. The list of options in a drop-down with car models is filtered when a car make is selected. And, of course, filtering the list of values should happen instantaneously when the user modifies the filtering value.

The S.H.O.P. application allows the user to select the product category from a list box that shows all (relevant) product categories. The page also has a product area drop-down component. The product area picked filters the list of relevant product categories. Whenever the product area selection changes, the list of product categories should be refreshed.

By now the steps should start to feel familiar:

  • Have the product area drop-down autosubmit its changed value.
  • Have the bean providing the category values take the selected product area into account.
  • Have the product category list refresh at the end of the PPR request.

The components are configured on the page as follows:

< af:selectOneChoice id="areaSO" label="Area"                                          value="#{advertisement.productArea}"                                                                                                autoSubmit="true">                                                                             < f:selectItems value="#{areaAndCategoryProvider.productAreaSelectItems}"/>                                      < /af:selectOneChoice>                                  < af:selectOneListbox id="categorySO" label="Product Category"                                                  value="#{advertisement.productArea}"                                                                                                 partialTriggers="areaSO">                                                                                < f:selectItems value="#{areaAndCategoryProvider.productCategorySelectItems}"/>                          < /af:selectOneListbox>

The components use a managed bean configured as areaAndCategoryProvider:

< managed-bean>                  < managed-bean-name>areaAndCategoryProvider< /managed-bean-name>         < managed-bean-class>otn.shop.AreaAndCategoryProvider< /managed-bean-class>             < managed-bean-scope>session< /managed-bean-scope>             < managed-property>               < property-name>advertisement< /property-name>                < property-class>otn.shop.ProductPosting< /property-class>                < value>#{advertisement}< /value>              < /managed-property>       < /managed-bean>

based on the otn.shop.AreaAndCategoryProvider class. The most interesting methods in this class are getProductAreaSelectItems() and getProductCategorySelectItems(), which return the selected items for the drop-down for areas and the list box for categories. The getProductCategorySelectItems() method uses the currently selected productArea in the advertisement bean—if any—to filter the list of product categories returned.

 List< String> productAreas = new ArrayList();      Map < String, List > productCategories = new HashMap< String, List >();      public SelectItem[] getProductCategorySelectItems() {          List< String> categories = new ArrayList < String>();          if (this.getAdvertisement().getProductArea() != null &&              this.getAdvertisement().getProductArea().length() > 0) {              categories.addAll(productCategories.get(this.getAdvertisement().getProductArea()));          } else {                  for (String area : this.productAreas) {                      categories.addAll(productCategories.get(area));                 }          }         SelectItem[] items = new SelectItem[categories.size()];          int i = 0;          for (String category : categories) {              items[i++] = new SelectItem(category, category);          }          return items;     }

See the downloadable source code for the initialization of the productAreas list and the productCategories map, which populates these constructs with actual readable values.

When we run the page, we can select a product area from the drop-down and a category from the list box. As soon as we select the product area, the product category list box is refreshed accordingly to show only the product categories that fit in the selected area (see Figure 13).

Figure 13 Filtering for the desired product areas - selecting an area changes the list of categories

This is a simple example of a recurring theme: a set of allowable values that is restricted by another component. The combination of autoSubmit—for sending the new filter value in a PPR to the server—with appropriate logic to apply the filter value when preparing the set of allowable values—and the partialTriggers attribute makes the component with the set of allowable values refresh.

The page ProductAdvertisment_step2.jspx in the JDeveloper application provided with this article demonstrates the cascading list feature.

Using PPR to Color-Coordinate and Provide Appropriate Context

We would like to brighten up the S.H.O.P. application a little. First, each product area should have its own theme color that is applied to the box that holds the product area and product category selector. The theme color for the Garden product area is obviously a lush green, Toys has pink, Cars has a chromelike color, and Furniture gets a tan theme.

Applying a color to a component is done in Oracle ADF Faces RC with the background-color setting in the contentStyle attribute. The panelGroupLayout component for which contentStyle is set is tied to the product area selection (changes) through its partialTriggers attribute.
 < af:panelGroupLayout id="areacatPGL" layout="horizontal"                            valign="top" halign="left"                                                                    partialTriggers="areaSO"                                                                    inlineStyle="backgroundcolor:                                      #{(advertisement.productArea=='Toys')?'pink'                                        :(advertisement.productArea=='Garden')?'green'                                        :(advertisement.productArea=='Cars')?'silver'                                        :(advertisement.productArea=='Furniture')?'tan':''                                       };                                     border-color:Gray; border-width:thin; padding:10px;">                                                                               … content            < /panelGroupLayout>

The page looks all that much more interesting for it (see Figure 14):

Figure 14 Changing colors as part of the Partial page refresh

Furthermore, it seems to be a nice touch to also display little theme pictures that go with the product area. The Images folder contains four images—Furniture.jpeg, Cars.jpeg, Toys.jpeg, and Garden.jpeg—that will be displayed to reflect the current product area. To accomplish this, we use an image component. Its source attribute refers to the JPEG file that should be displayed, using an EL reference to the selected product area. The partialTriggers attribute setting makes this component refresh whenever the product area selection is modified.

< af:image id="areaIM"             source="images/#{advertisement.productArea}.jpeg"             partialTriggers="areaSO"             inlineStyle="width:150px; border-color:Gray; border-width:2px;"/>

Adding this component has the effect shown in Figure 15 on the page:

Figure 15 Effect of adding an image component that is updated by the ppr action

Buttons or Links Can Trigger Partial Refresh with partialSubmit

Until now we have discussed only PPR requests that are sent because of a change in a component marked as autoSubmit. There is another trigger for PPR requests: a click on a command button or an action link. Normally, clicking a command link or button would lead to a full page refresh and reload. When we set the partialSubmit attribute on the button or link, however, that behavior is changed. Instead of a normal form submit or page navigation, the button or link initiates a PPR request. All form values are submitted, and all are processed through the normal lifecycle. Note that this is different than with autoSubmit, where only the changed component itself is processed, along with all components with a reference in their partialTriggers attribute. This also means that clicking a button or link with partialSubmit set will be successful only when all the required components in the form have been given a value.

Suppose we want to enable the user to show or hide the image we have just added to illustrate the product area. We can provide a command link that the user can click to toggle the visibility of the image. The command link has partialSubmit set to true. Both commandLink and the image are to be refreshed and have the command link's ID in their partialTriggers attribute:

< af:commandLink id="showImageCL"                              text="#{areaAndCategoryProvider.showAreaImage?'Hide image':'Show image'}"                                                         actionListener="#{areaAndCategoryProvider.toggleImage}"                                                         partialSubmit="true" partialTriggers="showImageCL"/>                                        < af:image id="areaIM"                  source="images/#{advertisement.productArea}.jpeg"                                        visible="#{areaAndCategoryProvider.showAreaImage}"                                        partialTriggers="areaSO showImageCL"                                        inlineStyle="height:75px; border-color:Gray; border-width:2px;"/>

The page will appear without the image and without the Show image/ Hide image command links. Only when a product area is selected will the “Show image” link appear. When that is clicked, the appropriate image is displayed and the link changes its text to “Hide image,” as shown in Figure 16.

Figure 16 The show image and hide image command links

By the way, we could implement this particular bit of functionality more efficiently by using client-side means only. This example is used to illustrate what partialSubmit can do.

To see the style manipulation and this partial submit in action, run the page ProductAdvertisment_step3.jspx in the JDeveloper application provided with this article.

Using autoSubmit and PPR in Rich Table Components

Another common use case for PPR is the calculation of totals in a table. A count or aggregation over all rows, displayed in a column footer is an example of this. Of course, row-level calculations, such as calculating the total price based on unit price times quantity, is frequently used as well.

Using PPR in tables is not hugely different from what we have seen so far, but it is a good opportunity to introduce the partial triggers editor that supports the selection of the triggering component and knows how to deal with the ID values inside various naming containers. For example, JSF will add :0, :1, :2, … postfixes to every component that is in a table row to clearly identify a component in one column that is rendered multiple times. Furthermore, components in various areas on the page may be prefixed with the ID of their enclosing container component. This means that straightforward references to the ID value of the triggering component—as we have used thus far—are not always valid. The partial triggers editor knows about naming containers, ID creation, and the proper way to construct the partialTriggers attribute.

In Figure 17, we have a page that lists several products that are being offered at S.H.O.P. The table allows editing of the Year and Price cells in the table. When the year is changed, the Age cell for that product listing should undergo a PPR. The Price column shows the average price for all products listed. When the Price cell for any product is modified, that average value should be refreshed as well.


Figure 17 Refreshing column footer value when cells are modified

Let's first look at the Year cell. When that is changed, a PPR request should follow that refreshes the Age cell. This means we set autoSubmit=”true” on the year inputText component. However, what should we set in the partialTriggers attribute for the Age cell? Something like “prodYearCol:0 prodYearCol:1 prodYearCol:2 ….” for all possible ID values? Or should we simply refresh the table as a whole?

If we go to the Property palette for the prodAgeOT outputText component and locate the partialTriggers attribute, we can open the partialTriggers editor from the little menu that opens after we click the icon next to the PartialTriggers field (see Figure 18):

Figure 18 Opening the partialtriggers editor from the property inspector for the average price component

This editor gives an overview of all components on the page and enables us to select them forthe partialTriggers attribute. Thus we locate the year inputText component that triggers refresh of the age component and add it to the selection. It turns out that within the table row, we can refer to other cells by simply using the component ID without the :0, :1 … postfix; the framework is smart enough to work out which cell should be refreshed.

The same goes for the average price outputText: we want it refreshed whenever any of the Price cells is modified. Again, using the PartialTriggers editor, we locate the priceIT component and add it to the partialTriggers attribute (see Figure 18). It turns out that even though the average price is rendered only once in the table and not in each individual table row, it can have a simple reference to priceIT in the partialTriggers attribute.

Figure 19 Any change in a price is reflected in a PPR update of the Average Price

If we want changes in the table to be reflected outside the scope of the table component, the reference in partialTriggers will become slightly more complicated. Say, for example, that we would like the panel header to reflect the average price as well: we have to configure the Panel Header component like this:

 < af:panelHeader id="productPH"                               text="Product Postings (average price #{postingsManager.averagePrice})"                                         size="-1"                                 partialTriggers="prodlistTable:priceIT">
   
The page ProductAdvertismentsList.jspx in the JDeveloper application provided with this article demonstrates the partial page refresh in action inside a table.

Using Auto-PPR for Automatically Refreshing Data-Bound Components



Using PPR, either declaratively—through autoSubmit or partialSubmit and partialTriggers—or programmatically, can sometimes require a lot of configuration. Life can be even easier than that, though. We can instruct Oracle ADF to automatically perform PPR on all data-bound items whose underlying value bindings change, by specifying a single property on the underlying iterator.

You can set the changeEventPolicy attribute to ppr on individual value bindings. Doing so means that anytime the associated component's value changes as a result of back-end business logic, the component will be automatically rerendered. You can also set an iterator binding's changeEventPolicy to ppr. When you do this, any action or value binding associated with the iterator will act as though its changeEventPolicy is set to ppr. This enables the entire form to use PPR without your having to configure each component separately.

When you drop a form and elect to include navigation controls, Oracle JDeveloper aut. Oracle JDeveloper also sets the partialSubmit attribute of each of the navigation buttons to true. This is how command components notify the framework that PPR should occur. When you set the navigation command components' partialSubmit attribute to true and the iterator's changeEventPolicy to ppr, all the components in the form that use the iterator binding will be rerendered each time a navigation button is clicked.

Conclusion

Using Ajax and partial-page refresh is the way to Web 2.0 applications with attractive, dynamic, and responsive user interfaces. The PPR functionality in Oracle ADF Faces RC can easily be put to use for a range of frequent use cases. With a simple declarative setting, we cause field value changes to be communicated to the server. And with another declarative setting, we instruct Oracle ADF to refresh components in the clients. This lays the foundation for more scenarios such as cascading lists of values, show/hide toggle, table column total calculation, and instant validation and conversion.

In Part 2 of this series, we will expand that range with more-advanced business cases that include other types of client events, implementation of an autosuggest feature, use of a pop-up with context-sensitive details, and integration of Google Maps.

This article is loosely based on a unit in the manual for the Oracle ADF 11g training jointly developed by SAGE Computing (Australia) and AMIS (The Netherlands).


Lucas Jellema is Technology Manager with AMIS in Nieuwegein, The Netherlands, and is an Oracle ACE Director (Oracle Fusion Middleware). Lucas is a frequent blogger, author, and presenter at international conferences and workshops. He earns most of his money doing teaching and consulting around the Oracle SOA Suite and ADF technology.

Chris Muir ( http://one-size-doesnt-fit-all.blogspot.com) is an Oracle ACE Director (Oracle Fusion Middleware) and a senior consultant and Oracle trainer for SAGE Computing Services in Australia. With a combined 40 years of experience in Oracle development and database technology, both show battle scars with years of experience in the RDBMS, traditional Oracle development tools, as well as Oracle Application Express, Oracle JDeveloper, and good old SQL*Plus.