Monitoring AJAX request lifecycle in JSF

Sometimes when working with AJAX in JSF you may find it useful to able to perform some actions before the request is sent to server, after the response is received and when some error occurs. The common case is to inform the user that some operation is in progress and may take few seconds or that the communication with server failed for some reason.

Event listeners

JSF provides optional attribute onevent for <f:ajax> tag which can point to JavaScript function that will be called at certain points of the execution of AJAX request. The prototype of the function looks like this:

function myEventListener(data) {
...
}

Argument data contains the following attributes:

  • status – Current phase of the execution of the AJAX request. Either begin, complete or success
  • type – Either event or status
  • source – The element that is the source of the AJAX request
  • responseXML – The response for the AJAX request
  • responseText – The response for the AJAX request in text format
  • responseCode – The numerical response code for the AJAX request

The begin status means that the request is about to be sent to the server, complete that the response was received and the web page is about to the rendered and success that the request was completed successfully including rendering. Note that complete does not mean that everything went fine and may also appear in case of error during rendering.
For obvious reasons responseXML, responseText and responseCode are not available when status of the AJAX request is begin.

Error listeners

JSF also provides optional attribute onerror which is quite similar to onevent but it points to the JavaScript function that will be called when error occurs during processing of AJAX request. The function also accepts only one argument data which has the same attributes. However, the possible values of status attribute are:

  • httpError – HTTP status
  • emptyResponse – Empty response was received
  • malformedXML – The response from server is not valid XML document
  • serverError – The server failed during execution of the request

Complete example

To show these 2 attributes in action, let’s take a look at a simple application that calculates and prints the value of constant pi after clicking button.

JavaScript function calculatePiListener specified by attribute onevent will be called three times at different phases of execution of AJAX request. In case of error it will be called maximum 2 times but additionally function specified by attribute calculatePiError will be called with details of the error. In either case the current status of the AJAX request will be printed at the bottom of the page.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>#{msgs.ajaxEvents}</title>
        <h:outputScript library="default" name="js/scripts.js" />
    </h:head>
    <h:body>
        <h:form prependId="false">
            <h:panelGrid columns="2">
                <h:commandButton value="#{msgs.calculatePi}" action="#{valueCalculator.calculatePi}">
                    <f:ajax event="action" execute="@this" render="piValueText"
                            onevent="calculatePiListener" onerror="calculatePiError" />
                </h:commandButton>
                <h:outputText id="piValueText" value="#{valueCalculator.hasPiValue ? valueCalculator.piValue : ''}" />
            </h:panelGrid> <br/>
            
            <h:outputText id="status" style="font-style: italic;"/>
        </h:form>
    </h:body>
</html>

Both JavaScript functions are defined in resources/default/js/scripts.js file as follows:

function calculatePiListener(data) {
    var statusElement = document.getElementById("status");
    if (data.status === "begin")
        statusElement.innerHTML = "Sent request. Waiting for response...";
    else if (data.status === "complete")
        statusElement.innerHTML = "Response received";
    else if (data.status === "success")
        statusElement.innerHTML = "Rendered successfully";
}

function calculatePiError(data) {
    var statusElement = document.getElementById("status");
    statusElement.innerHTML = "<b>Error</b> <i>" + data.status + " "
            + data.description + "</i>";
}

Because typically AJAX requests are very fast, the intermediate statuses would not normally be visible. To change it we ensure that calculatePi will execute for few seconds using the worst possible algorithm with very large number of iterations:

package com.example.ajaxevents;

import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named
@SessionScoped
public class ValueCalculator implements Serializable {

    private static final long serialVersionUID = 23487237489324L;
    private Double pi = null;

    public String calculatePi() throws InterruptedException {
        double sum = 0;
        double mul = 1;
        for (int i = 1; i < 100000000; i++) {
            sum += mul / (2 * i - 1);
            mul = -mul;
        }
        
        pi = 4 * sum;
        return null;
    }

    public Double getPiValue() {
        return pi != null ? pi : 0;
    }

    public boolean getHasPiValue() {
        return pi != null;
    }
}

Conclusion

Monitoring life-cycle of AJAX requests is a very useful technique because they let you inform the user that a time-consuming action is in progress. You have also possibility to inform the user that some request failed to execute.

Complete code for this example was tested on Glassfish and is available at GitHub.

Advertisement

About Robert Piasecki

Husband and father, Java software developer, Linux and open-source fan.
This entry was posted in AJAX, Java, Java EE, JSF and tagged , , , , . Bookmark the permalink.

1 Response to Monitoring AJAX request lifecycle in JSF

  1. Cláudio says:

    Thanks a lot, man!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.