The order of events in XPages

After reading another question on the XPages forum about which (serverside) events are triggered if you use the browsers “back” button on on XPage, I decided to run some tests.

First thing I did was to set up a simple test XPage (page1.xsp):

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

<xp:this.beforePageLoad>
<![CDATA[#{javascript:print("beforePageLoad")}]]>
</xp:this.beforePageLoad>
<xp:this.afterPageLoad>
<![CDATA[#{javascript:print("afterPageLoad")}]]>
</xp:this.afterPageLoad>
<xp:this.afterRestoreView>
<![CDATA[#{javascript:print("afterRestoreView")}]]>
</xp:this.afterRestoreView>
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:print("beforeRenderResponse")}]]>
</xp:this.beforeRenderResponse>
<xp:this.afterRenderResponse>
<![CDATA[#{javascript:print("afterRenderResponse")}]]>
</xp:this.afterRenderResponse>

<xp:link escape="true" text="Go to page 2" id="link1">
<xp:this.value><![CDATA[#{javascript:"page2.xsp"}]]></xp:this.value>
</xp:link>

</xp:view>

The order of events triggered when opening this XPage is as expected:

  • beforePageLoad
  • afterPageLoad
  • beforeRenderResponse
  • afterRenderResponse

 

Note that the afterRestoreView event isn’t executed: that event gets only executed after a (partial) refresh of the page.

If I open another XPage (page2.xsp) from my sample page using the link on page 1 and then click the browser’s back button (or a clientside link calling history.go(-1) ), no events are executed at all. That’s because the page is served from the browser cache, despite the fact that a Expires header is added by default with a value of -1 !

 

Control caching using a Cache-Control header

 

The Expires header is one way to control caching, the Cache-Control is another, so I checked what would happen if I added that one. I added this to the beforePageLoad event of page1:

var exCon = facesContext.getExternalContext();
var response=exCon.getResponse();
response.setHeader("Cache-Control", "no-cache");

That didn’t change anything, so I tried another:

response.setHeader("Cache-Control", "no-store");

(read this for the difference between no-cache and no-store)

That changed something: using the back button now causes page1 to execute the same list of events as mentioned before. So it seems that the no-store header actually causes the XPage not to be cached at all and opening an XPage using the back button had the same effect as initial opening it.

onClientLoad

With that in mind I tried something else: I added a serverside onClientLoad event to page 1:

<xp:eventHandler event="onClientLoad" submit="true" 
  refreshMode="norefresh" execMode="partial">
<xp:this.action><![CDATA[#{javascript:print("ocl");}]]></xp:this.action>
</xp:eventHandler>

As you can already see from the syntax, this event is different from the other XPage events. If you now open page 1, the XPage instantly performs a POST request to handle the onClientLoad event. That in turn causes the beforeRenderRespons, afterRenderResponse and afterRestoreView events to be executed.

If I know open page 2 and click the browser’s back button those events are also executed. So by adding an onClientLoad event you can force events to be executed (with the downside of an extra POST request on every access of the page).

Multi-page messages in XPages

The JSF framework on which XPages are built has built-in functions to display messages to users. You can see this in use if you add a validator to a field and disable clientside validation in the application properties: if the validation fails, a message is added to the FacesContext object that can be displayed on screen by including a Display Errors (<xp:messages>) control.

It is also possible to add your own custom messages by using the facesContext.addMessage() method. This was described by Tommy Valand on his blog.

A problem with the default JSF messages implementation is that the messages are gone if you open another page or redirect the user to another page. Consider for instance the case that you show a list of items to users on an XPage (using a data table or view panel) and allow a user to create a new item by redirecting him to another XPage containing a form. After the user has saved the form, you want to show him a confirmation message that the item was successfully saved or show him the ID of the created document. To be able to do this you need a way to store the messages between pages.

After some searching I found a way to do so by implementing a so called phase listener. This is basically a plugin you add to an application that allows you to execute your own custom code on predetermined phases in the JSF request lifecycle. The MultiPageMessagesSupport class from the article attaches itself to certain phases to temporarily store the messages in the sessionScope retrieve them when a (new) page is rendered.

To use this method you need to perform the following steps:

  • Create a new Java class in the “Code” section of your application (assuming you’re using 8.5.3). Call it MultiPageMessagesSupport, set the default package and add this code. The class file in the link uses the package “myPackage”.
  • Add the phase-listener from the newly created Java class to the faces-config.xml file of your application:
  myPackage.MultiPageMessagesSupport
  • Include an <xp:messages> control on all pages to show the messages to your users:
<xp:messages id=”messages1″ globalOnly=”true” layout=”list” styleClass=”messages”></xp:messages>
If you want to add a message you simply use the following code:
var type = javax.faces.application.FacesMessage.SEVERITY_INFO;
var msg = "this is my message";

facesContext.addMessage(null,
   new javax.faces.application.FacesMessage(type, msg, ""));
where “type” is one of the following:
  • javax.faces.application.FacesMessage.SEVERITY_INFO
  • javax.faces.application.FacesMessage.SEVERITY_WARN
  • javax.faces.application.FacesMessage.SEVERITY_ERROR
  • javax.faces.application.FacesMessage.SEVERITY_FATAL

I’ve created an online demo that uses the OneUI v2.1 theme (and associated classes for different type of messages). Thanks again to the people over at ClearIT Consulting for hosting this!.

The demo database can be downloaded here.