Forcing refresh of an ADF Faces component with EVERY Partial Page Request html

Forcing refresh of an ADF Faces component with EVERY Partial Page Request

Even when JSF was not around and ADF Faces was still called UIX, Oracle already had the cool “Partial Page Request” (PPR) feature. What this boils down to is that when a certain component in the page issues a “partial event” , an (AJAX-like) request will be sent to the server that (by default) only refreshes the component itself based on the server response, rather than refreshing the entire page. What makes this feature really useful, however, is that you can also specify that other components in the same page can be refreshed along with the component issuing the “partial event”. Take the classic “dependent poplists” case, for example a poplist with “Car Manufacturer” and a second poplist “Car Model” that only shows the cars made by the selected manufacturer. When that first “manufacturer” poplist changes, we need to refresh the values in the “model” poplist as well (while the rest of the page remains in place and unchanged).

“partialTargets” vs “partialTriggers”

When it comes to declaring this dependency between those two components in the page, there’s basically two ways you can go about it. You can have the triggering component indicate which other component(s) need to be refreshed along with it, OR you can have the dependent component(s) indicate which component(s) in the page will trigger it to refresh. Back in the UIX days they chose the first option, meaning that in our example the triggering component (the “manufacturer” poplist) would specify (through the “partialTargets” property) that the “car model” poplist needs to be refreshed along with it. In ADF Faces, they turned this around, and now the “car model” poplist needs to indicate (using the “partialTriggers” property) that it gets triggered by the “manufacturer” poplist.

While this seems an arbitrary choice, there can be significant consequences. For instance, consider the common scenario where every page in your application has a generic “messages” component at the top, that renders (fault) messages for the end user (if there are any). Since partial requests might also result in fault messages that the end user would need to see, the “messages” component needs to be redrawn with every PPR request. In the UIX days, this meant that every component in the page that could issue partial events needed include the “message” component in its “partialTargets” property. In ADF Faces, however, it is the other way around, and the “messages” component needs its “partialTriggers” property to hold a long list of ALL components in the page that issue PPR requests. In my opinion, there are some significant problems with this:

  1. The “messages” component is a fairly “generic” component that should be defined in exactly the same way on each and every page. It would therefore be a good candicate for being “templated” or “referenced in”, rather than defining it again and again in every page. However, the value of the “partialTriggers” property would be _VERY_ page-specific, as it lists all kind of page-specific components.
  2. Maintaining/building the page is more laboursome, because every time you add a component that can issue partial events you’ld need to remember to add it to the “partialTriggers” property of the “messages” component. Likewise, if you remove a component, you’ld also have to remove it from the list in that property.
  3. Failure to “register” a new PPR-enabled component in a page with the “messages” component would not lead to any errors and would in all likelyhood not be captured by testers either, but _IF_ error messages are added during a PPR request issued by that new component, you’ld have a hard time finding out about it as the messages component would show you nothing.

The ADF Faces team must have noticed this awkward situation as well, because “miraculously” the <af:messages> component that is meant for exactly the purpose of displaying the user messages in the page, WILL be refreshed with every PPR request, without the need of specifying the “partialTriggers” property. But what if you need to do something like that as well?

Having a Component refresh automatically with every Partial Request

I recently ran into this situation, when I had to add component to each and every page of my application which needed to be refreshed with every PPR request. I hated the fact that I would have to provide the “partialTriggers” property for every individiual page, and looked for an easier, generic solution. After all, it was a “generïc” component, with “generic” behaviour..

The component in question was a sort of “global status message box”, that needed to be rendered if such a message was present. In the JSPX file, it looked like this:

  <af:panelGroup partialTriggers="[here a list of all components in the current page that issue PPR requests]">
    <af:panelBox text="#{GlobalStatus.statusMessage}" rendered="#{GlobalStatus.hasMessage}"
  </af:panelGroup>

Behind it is a (request scoped) Backing Bean that has access to this “global status message”:

<managed-bean>
  <managed-bean-name>GlobalStatus</managed-bean-name>
  <managed-bean-class>nl.pebell.GlobalStatusBean</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <managed-property>
   <property-name>bindings</property-name>
   <value>#{bindings}</value>
  </managed-property>
</managed-bean>

The (heavily simplified) class behind it looked like this:

public class GlobalStatusBean
{
   private BindingContainer bindings = null;
   private String statusMessage = null;
  
   public String getStatusMessage()
   {
     //some code to retrieve the global status message
     statusMessage = ....;
     return statusMessage;
   }
 
   public boolean isHasStatusMessage()
   {
     return (getStatusMessage() != null);
   } 
 
   public void setBindings(BindingContainer bindings)
   {
     this.bindings = bindings;
   }
 
   public BindingContainer getBindings()
   {
     return bindings;
   }
 
}

With this code, everything worked fine as long as I would provide the correct value for the “partialTriggers” property in each and every page. As I really REALLY did not want to do that, I decided to find out if I could omit the partialTriggers property at design time, and rather set it at runtime! This turned out to be really simple to implement in the backing bean I already had in place. First, I removed the “partialTriggers” property, and instead, I used the standard JSF “binding” property to provide a reference in the backing bean to the Java UI Component class behind the <af:panelGroup>:

  <af:panelGroup binding="#{GlobalStatus.uiComponent}">
    <af:panelBox text="#{GlobalStatus.statusMessage}" rendered="#{GlobalStatus.hasMessage}"
  </af:panelGroup>

Of cource, I now needed to add the get/setUiComponent methods to the backing bean:

public class GlobalStatusBean
{
  private BindingContainer bindings = null;
  private String statusMessage = null;

  private CorePanelGroup uiComponent;

  public void setUiComponent(CorePanelGroup uiComponent)
  {
    this.uiComponent = uiComponent;
  }

  public CorePanelGroup getUiComponent()
  {
    return uiComponent;
  }

  ...
}

And now for the real trick: as the GlobalStatus bean is request-scoped, the “setUiComponent()” method gave a the perfect “hook” to dynamically set the “partialTriggers” property on the <af:panelGroup>. I made use of two request parameters that ADF Faces uses to implement the PPR behaviour: “partial=true”, and “source=[The id of the component triggering the PPR]”:

  public void setUiComponent(CorePanelGroup uiComponent)
  {
    this.uiComponent = uiComponent;
    HttpServletRequest req = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
    String partial = (String)req.getParameter("partial");
    if ("true".equals(partial))
    {
      String source = (String)req.getParameter("source");
      uiComponent.setPartialTriggers(new String[] {source});
    }
  }

So in other words, every time a PPR request occurs, I dynamically set the “partialTriggers” property of my <af:panelGroup> component to exactly the component that issued the partial request, causing the ADF Faces framework to refresh it. Works like a charm…


2 Comments

  1. microraptor February 14, 2009
  2. Koen Verhulst August 25, 2008