In this exercise we will use an action in a backing bean to control navigation.
Before we start, add another JSF file to your project, called "newgown" (i.e., newgown.xhtml).
Use this as the source of the file newgown.xhtml:
<?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">
<h:head>
<title>Bad Advice</title>
</h:head>
<h:body>
<p>Buy a new dressing gown.</p>
<p>
<h:button value="Ok" outcome="question"/>
</p>
</h:body>
</html>
Backing Beans
A backing bean is a Java Bean that has been annotated with @Named.
JavaServer Faces uses the @Named annotation to refer to the backing bean by name.
By default the name of the bean is the same as the name of the class, where the first letter has been made lowercase:
- e.g., The class
MyBean will use name myBean
- e.g., The class
AnotherBean will use name anotherBean
- e.g., The class
QuestionController will use name questionController
In addition to the @Named annotation, you will need to introduce the relevant scope annotation.
The scope tells the application server how many instances to create and when to create them.
Here are some of the scopes you can use:
@ApplicationScoped: one instance of the bean is created for the entire application
@RequestScoped: one instance of the bean is created per request
@SessionScoped: one instance of the bean is created per user session
@Dependent: a new instance of the bean created every time and in every expression that it is used
There are other scopes, but we will not cover them here. Feel free to research on your own.
We will focus on RequestScoped as it helps understand the operation of JavaServer Faces.
Create a Backing Bean
Create a new Java class named "au.edu.uts.aip.advice.QuestionController":
- Create a file of type "Java Class" in the "Java" category.
- Enter the class name "QuestionController" and package name "au.edu.uts.aip.advice".
Copy the following code into the Java file:
package au.edu.uts.aip.advice;
import javax.inject.*;
import javax.enterprise.context.*;
@Named
@RequestScoped
public class QuestionController {
public String doSuggestion() {
if (Math.random() < 0.5) {
return "morecats";
} else {
return "newgown";
}
}
}
Note that the class is annotated by @Named and @RequestScoped.
Reflect
Do you understand every line of code in this file?
Using the Backing Bean
Now, we will edit question.xhtml so that the action for the "No" button calls createSuggestion.
To do this, we need to replace the button with a commandButton (or a commandLink).
The commandButton currently looks something like the following:
<h:button value="No" outcome="morecats"/>
Change that line so that it now looks like the following:
<h:commandButton value="No" action="#{questionController.doSuggestion}"/>
There are three things to note here in JavaServer Faces:
- In JavaServer Pages, Expression Language uses a dollar sign: ${expr}
In JavaServer Faces, Expression Language uses the hash symbol: #{expr}
- Our bean was in a class called
QuestionController, so JavaServer Faces finds the backing bean using the name "questionController".
- We are referring to a method, not invoking the method. We don't need to use brackets to call the function. (However, there is no harm if you do use the brackets.)
i.e., questionController.doSuggestion, rather than questionController.doSuggestion()
Finally, for the commandButton to work, it also needs to be inside a <h:form>.
i.e., your final file for question.xhtml should look something like this:
<?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">
<h:head>
<title>Bad Advice</title>
</h:head>
<h:body>
<h:form>
<p>Are you happy?</p>
<p>
<h:button value="Yes" outcome="good"/>
<h:commandButton value="No" action="#{questionController.doSuggestion}"/>
</p>
</h:form>
</h:body>
</html>
Run the JSF Application
Once again, right click on question.xhtml and click "Run File".
Whenever you answer "No", it should randomly choose between the two possible outcomes.
Note: Whenever you change your Java source code, you should right click on the project to choose "Clean". Then select your file and click run. This helps avoid errors that can sometimes occur when redeploying code over an already running project.
Reflect
Carefully notice what happens to the URL in your browser whenever you click on "No".
Does the content of the page match what is suggested by the URL?
What does this tell us about JavaServer Faces?
Using Redirects
In the backing bean, change the responses to include faces-redirect=true.
In other words, in QuestionController.java, replace lines that look like this:
return "morecats";
with lines that look like this:
return "morecats?faces-redirect=true";
(You should do it for both morecats and newgown)
Right click on the project to choose "Clean". Then right click on question.xhtml click run.
What happens now with the URLs in your browser when you click on "No"?
Using Redirects in Navigation
Undo those changes you made to the backing bean (i.e., go back to just returning "morecats" and "newgown").
Now, add new navigation rules to faces-config.xml:
- If you wish, you can delete any of the existing navigation rules because our code currently uses direct navigation. To delete a navigation rule, click on the arrow in the PageFlow editor and press delete.
- Use the visual editor to draw an arrow from question.xhtml to morecats.xhtml, and label the arrow "morecats" (without the quotes)
- Draw another arrow from question.xhtml to newgown.xhtml, and label the arrow "newgown" (without the quotes).
- Switch to the source of faces-config.xml (click on the tab labeled "Source" at the top left of the PageFlow editor).
- Find the
<navigation-rule> for question.xhtml and in the navigation cases for morecats and newgown, add a <redirect/> element (this tag should go inside the <navigation-case> element).
Your faces-config file should end up looking something like this:
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<navigation-rule>
<from-view-id>/question.xhtml</from-view-id>
<navigation-case>
<from-outcome>morecats</from-outcome>
<to-view-id>/morecats.xhtml</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>newgown</from-outcome>
<to-view-id>/newgown.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
</faces-config>
Save any changes, and once again run question.xhtml.
What happens now with the URLs in your browser when you click on "No"?
Even though we have removed "?faces-redirect=true" from the outcome, Faces is still redirecting the responses so that the URL matches the content.