Asynchronously invoking a second managed bean method in JavaSever Faces (JSF)
The new features added to JSF make doing an asynchronous call to the server incredibly easy, but what if you need to follow that first asynchronous call with a second asynchronous call to a JavaServer Faces managed bean? It's a bit of work, but in this tutorial, we walk you through it.
A cohort of mine had a JavaServer Faces application that needed the ability to asynchronously call the method of a backing bean after a given JSF commandButton or commandLink had completed its asynchronous call. Basically, a method of a managed bean needed to be invoked after another managed bean method had been invoked asynchronously, and that first asynchronous call had completed successfully. It’s not a difficult problem to solve with basic HTML, JavaScript and Servlets or JSPs waiting to be invoked on the server side, but trying to accomplish this with JSF is actually somewhat complex.
Regardless of the type of plumbing that needs to be put in place to complete this user-story, there’s not arguing about the fact that a backing bean with two methods needs to be created. We’ll uncreatively call the managed bean MyManagedBean and the two methods will be doSomething and doSomethingElse:
package com.mcnz.jsf;
import javax.faces.bean.*;
@ManagedBean @ViewScoped
public class MyManagedBean {
public void doSomething() {
System.out.println("doSomething method called.");
}
public void doSomethingElse() {
System.out.println("doSomethingElse method called.");
}
}
The next requirement is a web page, which we will name second-prong.xhtml, that uses a commandLink to asynchronously invoke the doSomething method. The command alone looks as follows:
<h:commandButton id="button" action="#{myManagedBean.doSomething}" value="click">
<f:ajax execute="link"/>
</h:commandButton>
The whole page, with all of the required tags is a little bit more verbose:
<html lang="en" xmlns="https://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:body>
<h:form id="form">
<h:commandButton id="button" action="#{myManagedBean.doSomething}" value="click">
<f:ajax execute="link"/>
</h:commandButton>
</h:form>
</h:body>
</html>
If you run your application and test the page, you should be able to click on the button and the doSomething method should asynchronously invoke the doSomething method, causing the text doSomething method called to be printed to the console.
The requirement is to invoke another managed bean method after the initial button click has been handled. To do this, we need to interact with the JSF framework’s handling of the Ajax lifecycle. That means playing around with some JSF JavaScript variables, and to do so, the following tag must be added to the xthml page:
<h:outputScript library="javax.faces" name="jsf.js" />
With this tag added, we can now play around with the Ajax lifecycle. We’ll show the code for interacting with both the begin and complete stages, but what we’re really interested in is invoking a managed bean method once the initial Ajax call has finished, making us interested primarily in the complete phase.
<h:outputScript library="javax.faces" name="jsf.js" />
<script type="text/javascript" >
function handleAjax(data) {
var status = data.status;
switch(status) {
case "begin":
alert("begin");
break;
case "complete":
alert("complete");
break;
}
} </script>
If you add the above script element to your second-prong.xhtml page, each time the click button is invoked, you will see two alert messages, one indicating that the Ajax call is about to begin, and another indicating that the Ajax call has completed. Of course, our goal isn’t to simply shoot out JavaScript alert messages. What we want to do is invoke another managed bean method.
To do so, we will follow the tips learned when we tackled the first prong of this problem, namely the problem of invoking a managed bean method through JavaScript. The full tutorial is here, but the key is adding a hidden command button and invoking that button through JavaScript. Here’s the HTML we need to add:
<h:commandButton style=“display:none” id=“elsebutton”
action=“#{myManagedBean.doSomethingElse}”>
<f:ajax/>
</h:commandButton>
Notice that the button is hidden with the display:none style attached. It also wraps an f:ajax tag, so this button will be executed asynchronously.
The next step is to have the elsebutton invoked through JavaScript once the Ajax call has completed. That means updating the complete branch of the case statement in the JavaScript code:
<h:outputScript library="javax.faces" name="jsf.js" />
<script type="text/javascript" >
function handleAjax(data) {
var status = data.status;
switch(status) {
case "begin":
break;
case "complete":
var jsfCommandLink = document.getElementById(“form:elsebutton”);
jsfCommandLink.click();
break;
}
}
</script>
And that’s it. With the code in place, not only with the commandButton call the doSomething method asynchronously when it is clicked, but when the Ajax call completes, the script placed in the complete branch of the case statement will execute, causing a second backing bean method to be invoked asynchronously. And that’s all there is to it. It’s a bit of work, but it’s reliable, and it will fulfill the requirements if an asynchronous call to the server is needed after a given commandButton or commandLink has been invoked.
Do you have another solution to the problem? Let us know.
You can follow me, Cameron McKenzie, on Twitter: @potemcam