Advanced Navigation Issues

Dynamic Navigation

In most web applications, navigation is not static. The page flow does not just depend on which button you click but also on the inputs that you provide. For example, submitting a login page may have two outcomes: success or failure. The outcome depends on a computation namely, whether the username and password are legitimate.

To implement dynamic navigation, the submit button must have a method expression, such as

  <h:commandButton label="Login" action="#{loginController.verifyUser}"/>

In our example, loginController references a bean of some class, and that class must have a method named verifyUser.

A method expression in an action attribute has no parameters. It can have any return type. The return value is converted to a string by calling toString.

Note

In JSF 1.1, an action method was required to have return type String. In JSF 1.2, you can use any return type. In particular, using enumerations is a useful alternative since the compiler can catch typos in the action names.


Here is an example of an action method:

  String verifyUser() {      if (...)         return "success";      else         return "failure";   }

The method returns an outcome string such as "success" or "failure". The navigation handler uses the returned string to look up a matching navigation rule.

Note

An action method may return null to indicate that the same page should be redisplayed.


In summary, here are the steps that are carried out whenever the user clicks a command button whose action attribute is a method expression:

  • The specified bean is retrieved.

  • The referenced method is called.

  • The resulting string is passed to the navigation handler. (As explained under "The Navigation Algorithm" on page 87, the navigation handler also receives the method expression string.)

  • The navigation handler looks up the next page.

Thus, to implement branching behavior, you supply a reference to a method in an appropriate bean class. You have wide latitude about where to place that method. The best approach is to find a class that has all the data that you need for decision making.

Next, we work through this process in an actual application. Our sample program presents the user with a sequence of quiz questions (see Figure 3-1).

Figure 3-1. A quiz question


When the user clicks the "Check Answer" button, the application checks whether the user provided the correct answer. If not, the user has one additional chance to answer the same problem (see Figure 3-2).

Figure 3-2. One wrong answer: Try again


After two wrong answers, the next problem is presented (see Figure 3-3).

Figure 3-3. Two wrong answers: Move on


And, of course, after a correct answer, the next problem is presented as well. Finally, after the last problem, a summary page displays the score and invites the user to start over (see Figure 3-4).

Figure 3-4. Done with the quiz


Our application has two classes. The Problem class, shown in Listing 3-1, describes a single problem, with a question, an answer, and a method to check whether a given response is correct.

The QuizBean class describes a quiz that consists of a number of problems. A QuizBean instance also keeps track of the current problem and the total score of a user. You will find the complete code in Listing 3-2.

Listing 3-1. javaquiz/src/java/com/corejsf/Problem.java

  1. package com.corejsf;   2.   3. public class Problem {   4.    private String question;   5.    private String answer;   6.   7.    public Problem(String question, String answer) {   8.       this.question = question;   9.       this.answer = answer;  10.    }  11.  12.    public String getQuestion() { return question; }  13.  14.    public String getAnswer() { return answer; }  15.  16.    // override for more sophisticated checking  17.    public boolean isCorrect(String response) {  18.       return response.trim().equalsIgnoreCase(answer);  19.    }  20. }

In this example, the QuizBean is the appropriate class for holding the navigation methods. That bean has all the knowledge about the user's actions, and it can determine which page should be displayed next.

Listing 3-2. javaquiz/src/java/com/corejsf/QuizBean.java

  1. package com.corejsf;   2.   3. public class QuizBean {   4.    private int currentProblem;   5.    private int tries;   6.    private int score;   7.    private String response;   8.    private String correctAnswer;   9.  10.    // here, we hardwire the problems. In a real application,   11.    // they would come from a database  12.    private Problem[] problems = {  13.       new Problem(  14.          "What trademarked slogan describes Java development? Write once, ...",  15.          "run anywhere"),  16.       new Problem(  17.          "What are the first 4 bytes of every class file (in hexadecimal)?",  18.          "CAFEBABE"),  19.       new Problem(  20.          "What does this statement print? System.out.println(1+\"2\");",  21.          "12"),  22.       new Problem(  23.          "Which Java keyword is used to define a subclass?",  24.          "extends"),  25.       new Problem(  26.          "What was the original name of the Java programming language?",  27.          "Oak"),  28.       new Problem(  29.          "Which java.util class describes a point in time?",  30.          "Date")  31.    };  32.  33.    public QuizBean() { startOver(); }  34.  35.    // PROPERTY: question  36.    public String getQuestion() {  37.       return problems[currentProblem].getQuestion();  38.    }  39.  40.    // PROPERTY: answer  41.    public String getAnswer() { return correctAnswer; }  42.  43.    // PROPERTY: score  44.    public int getScore() { return score; }  45.  46.    // PROPERTY: response  47.    public String getResponse() { return response; }  48.    public void setResponse(String newValue) { response = newValue; }  49.  50.    public String answerAction() {  51.       tries++;  52.       if (problems[currentProblem].isCorrect(response)) {  53.          score++;  54.          nextProblem();  55.          if (currentProblem == problems.length) return "done";  56.          else return "success";  57.       }  58.       else if (tries == 1) {  59.          return "again";  60.       }  61.       else {  62.          nextProblem();  63.          if (currentProblem == problems.length) return "done";  64.          else return "failure";  65.       }  66.    }  67.  68.    public String startOverAction() {  69.       startOver();  70.       return "startOver";  71.    }  72.  73.    private void startOver() {  74.       currentProblem = 0;  75.       score = 0;  76.       tries = 0;  77.       response = "";  78.    }  79.  80.    private void nextProblem() {  81.       correctAnswer = problems[currentProblem].getAnswer();  82.       currentProblem++;  83.       tries = 0;  84.       response = "";  85.    }  86. }     

Have a glance at the code inside the answerAction method of the QuizBean class. The method returns one of the strings "success" or "done" if the user answered the question correctly, "again" after the first wrong answer, and "failure" or "done" after the second wrong try.

   public String answerAction() {       tries++;       if (problems[currentProblem].isCorrect(response)) {          score++;          if (currentProblem == problems.length - 1) {             return "done";          }          else {             nextProblem();             return "success";          }       }       else if (tries == 1) {          return "again";       }       else {          if (currentProblem == problems.length - 1) {             return "done";          }          else {             nextProblem();             return "failure";          }       }    }     

We attach the answerAction method expression to the buttons on each of the pages. For example, the index.jsp page contains the following element:

  <h:commandButton value="Check answer" action="#{quiz.answerAction}"/>

Here, quiz is the QuizBean instance that is defined in faces-config.xml.

Figure 3-5 shows the directory structure of the application. Listing 3-3 shows the main quiz page index.jsp. The success.jsp and failure.jsp pages are omitted. They differ from index.jsp only in the message at the top of the page.

Figure 3-5. Directory structure of the Java Quiz application


The done.jsp page in Listing 3-4 shows the final score and invites the user to play again. Pay attention to the command button on that page. It looks as if we could use static navigation, since clicking the "Start over" button always returns to the index.jsp page. However, we use a method expression:

  <h:commandButton value="Start over" action="#{quiz.startOverAction}"/>

The startOverAction method carries out useful work that needs to take place to reset the game. It resets the score and reshuffles the response items:

   public String startOverAction() {       startOver();       return "startOver";    }

In general, action methods have two roles:

  • To carry out the model updates that are a consequence of the user action

  • To tell the navigation handler where to go next

Note

As you will see in Chapter 7, you can also attach action listeners to buttons. When the user clicks the button, the code in the processAction method of the action listener is executed. However, action listeners do not interact with the navigation handler.


Listing 3-5 shows the application configuration file with the navigation rules.

Because we selected our outcome strings so that they uniquely determine the successor web page, we can use a single navigation rule:

  <navigation-rule>      <navigation-case>         <from-outcome>success</from-outcome>         <to-view-id>/success.jsp</to-view-id>      </navigation-case>      <navigation-case>         <from-outcome>again</from-outcome>         <to-view-id>/again.jsp</to-view-id>      </navigation-case>      ...   </navigation-rule>

Figure 3-6 shows the transition diagram.

Figure 3-6. The transition diagram of the Java Quiz application


Finally, Listing 3-6 shows the message strings.

Listing 3-3. javaquiz/web/index.jsp

  1. <html>   2.    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>   3.    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>   4.   5.    <f:view>   6.       <head>    7.          <title><h:outputText value="#{msgs.title}"/></title>   8.       </head>   9.       <body>  10.          <h:form>  11.             <p>  12.                <h:outputText value="#{quiz.question}"/>  13.             </p>  14.             <p>  15.                <h:inputText value="#{quiz.response}"/>  16.             </p>  17.             <p>  18.                <h:commandButton value="#{msgs.answerButton}"   19.                   action="#{quiz.answerAction}"/>  20.             </p>  21.          </h:form>  22.       </body>  23.    </f:view>  24. </html>     

Listing 3-4. javaquiz/web/done.jsp

  1. <html>   2.    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>   3.    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>   4.    <f:view>   5.       <head>   6.          <title><h:outputText value="#{msgs.title}"/></title>   7.       </head>   8.       <body>   9.          <h:form>  10.             <p>  11.                <h:outputText value="#{msgs.thankYou}"/>  12.                <h:outputFormat value="#{msgs.score}">   13.                   <f:param value="#{quiz.score}"/>  14.                </h:outputFormat>  15.             </p>  16.             <p>  17.                <h:commandButton value="#{msgs.startOverButton}"  18.                   action="#{quiz.startOverAction}"/>  19.             </p>  20.          </h:form>  21.       </body>  22.    </f:view>  23. </html>     

Listing 3-5. javaquiz/web/WEB-INF/faces-config.xml

  1. <?xml version="1.0"?>   2. <faces-config xmlns="http://java.sun.com/xml/ns/javaee"   3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   4.    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   5.         http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"   6.    version="1.2">   7.    <navigation-rule>   8.       <navigation-case>   9.          <from-outcome>success</from-outcome>  10.          <to-view-id>/success.jsp</to-view-id>   11.          <redirect/>  12.       </navigation-case>  13.       <navigation-case>  14.          <from-outcome>again</from-outcome>  15.          <to-view-id>/again.jsp</to-view-id>  16.       </navigation-case>  17.       <navigation-case>  18.          <from-outcome>failure</from-outcome>  19.          <to-view-id>/failure.jsp</to-view-id>  20.       </navigation-case>  21.       <navigation-case>  22.          <from-outcome>done</from-outcome>  23.          <to-view-id>/done.jsp</to-view-id>  24.       </navigation-case>  25.       <navigation-case>  26.          <from-outcome>startOver</from-outcome>  27.          <to-view-id>/index.jsp</to-view-id>  28.       </navigation-case>  29.    </navigation-rule>  30.    <managed-bean>  31.       <managed-bean-name>quiz</managed-bean-name>  32.       <managed-bean-class>com.corejsf.QuizBean</managed-bean-class>  33.       <managed-bean-scope>session</managed-bean-scope>  34.    </managed-bean>  35.  36.    <application>  37.       <resource-bundle>  38.          <base-name>com.corejsf.messages</base-name>  39.          <var>msgs</var>  40.       </resource-bundle>  41.    </application>  42. </faces-config>     

Listing 3-6. javaquiz/src/java/com/corejsf/messages.properties

  1. title=A Java Trivia Quiz   2. answerButton=Check Answer   3. startOverButton=Start over   4. correct=Congratulations, that is correct.   5. notCorrect=Sorry, that was not correct. Please try again!   6. stillNotCorrect=Sorry, that was still not correct.   7. correctAnswer=The correct answer was: {0}.   8. score=Your score is {0}.   9. thankYou=Thank you for taking the quiz.



Core JavaServerT Faces
Core JavaServer(TM) Faces (2nd Edition)
ISBN: 0131738860
EAN: 2147483647
Year: 2004
Pages: 84

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net