Mastering JavaServer Faces 2.2
上QQ阅读APP看书,第一时间看更新

Passing and getting parameters

As you will see in the next sections, JSF provides several approaches to pass/get parameters to/from Facelets, managed beans, UI components, and so on.

Using context parameters

Context parameters are defined in the web.xml file using the <context-param> tag. This tag allows two important children: <param-name>, which indicates the parameter name, and <param-value>, which indicates the parameter value. For example, a user-defined context parameter looks like the following code:

<context-param>
  <param-name>number.one.in.ATP</param-name>
  <param-value>Rafael Nadal</param-value>
</context-param>

Now, in a JSF page, you can access this parameter as shown in the following code:

<h:outputText value="#{initParam['number.one.in.ATP']}"/>
<h:outputText value="#{facesContext.externalContext.initParameterMap['number.one.in.ATP']}"/>

In a managed bean, the same context parameter can be accessed via the getInitParameter method:

facesContext.getExternalContext().getInitParameter("number.one.in.ATP");

The complete application is named ch2_27.

Passing request parameters with the <f:param> tag

Sometimes, you need to pass parameters from a Facelet to a managed bean or to another Facelet. In this case, you may need the <f:param> tag, which can be used to add query string name-value pairs to a request, or put simply, to send request parameters. Commonly, the <f:param> tag is used inside the <h:commandButton> and <h:commandLink> tags for sending request parameters to a managed bean. For example, the following snippet of code adds two parameters to the request when the form is submitted. These parameters are accessed in the PlayersBean bean; the first parameter is named playerNameParam and the second one is named playerSurnameParam.

<h:form>
  Click to send name, 'Rafael' surname, 'Nadal', with f:param:
  <h:commandButton value="Send Rafael Nadal"  action="#{playersBean.parametersAction()}">
  <f:param id="playerName" name="playerNameParam" value="Rafael"/>
  <f:param id="playerSurname" name="playerSurnameParam" value="Nadal"/>
  </h:commandButton>
</h:form>

As you can see, when the button is clicked, the request parameters are sent and the parametersAction method is called (via action or actionListener). When the application flow reaches this method, the two request parameters are already available for use. You can easily extract them inside this method by accessing the request parameters map through the current FacesContext instance as shown in the following code:

private String playerName;
private String playerSurname;
...
//getter and setter
...

public String parametersAction() {

  FacesContext fc = FacesContext.getCurrentInstance();
  Map<String, String> params = fc.getExternalContext().getRequestParameterMap();
  playerName = params.get("playerNameParam");
  playerSurname = params.get("playerSurnameParam");
       
  return "some_page";
}

The values of both the parameters are stored in the playerName and playerSurname managed beans' properties (these can be modified further without affecting the original parameters), but you can easily display the parameters' values using the param EL reserved word in some_page (remember the EL implicit objects section of Chapter 1, Dynamic Access to JSF Application Data through Expression Language (EL 3.0), which explains that param is a predefined variable referring to the request parameter map):

Name: #{param.playerNameParam} 
Surname: #{param.playerSurnameParam}

The <f:param> tag can also be used inside the <h:outputFormat> tag to substitute message parameters; <f:param> is used to pass parameters to a UI component as shown in the following code:

<h:outputFormat value="Name: {0} Surname: {1}">
  <f:param value="#{playersBean.playerName}" />
  <f:param value="#{playersBean.playerSurname}" /> 
</h:outputFormat>

The preceding code's output is as follows:

Name: Rafael Surname: Nadal

Note

If you want to execute some initialization tasks (or something else) after setting the managed bean properties but before an action method is called (if it exists), then you can define a public void method annotated with @PostConstruct. In this example, the init method will be called before the parametersAction method, and the passed request parameters are available through the request map.

The init method is shown in the following code:

@PostConstruct
public void init(){
  //do something with playerNameParam and playerSurnameParam
}

This example is wrapped into the application named ch2_1.

If you think that it is not a very convenient approach to access the request map in the managed bean, then you can use @ManagedProperty, which sets the parameter as a managed bean property and links its value to the request parameter:

@ManagedProperty(value = "#{param.playerNameParam}")
private String playerName;
@ManagedProperty(value = "#{param.playerSurnameParam}")
private String playerSurname;

The values are set immediately after the bean's construction and are available during @PostConstruct, but keep in mind that @ManagedProperty is usable only with beans managed by JSF (@ManagedBean), not with beans managed by CDI (@Named).

This example is wrapped into the application named ch2_2 which is available in the code bundle of this chapter. You may also be interested in the application ch2_3, which is another example of using <f:param>, @ManagedProperty, and @PostConstruct. In this example, the <h:commandButton> action indicates another JSF page instead of a managed bean method.

The <f:param> tag can be used to pass request parameters directly between Facelets, without involving a managed bean. Usually, this happens in the <h:link> tag, as shown in the following code:

<h:link value="Send Rafael Nadal" outcome="result">
  <f:param id="playerName" name="playerNameParam" value="Rafael"/>
  <f:param id="playerSurname" name="playerSurnameParam" value="Nadal"/>               
</h:link>

When the Send Rafael Nadal link is clicked, JSF will use the prepared URL containing the result.xhtml file's resource name and the request parameters, playerNameParam and playerSurnameParam. Both the parameters are displayed in the result.xhtml file as follows:

Name: #{param.playerNameParam} 
Surname: #{param.playerSurnameParam}

If you check the URL generated by the <h:link> tag in the browser address bar, then you will see something like the following URL:

http://hostname/ch2_4/faces/result.xhtml?playerNameParam=Rafael&playerSurnameParam=Nadal

This example is wrapped into the application named ch2_4. In that application, you can also see an example using the <h:commandButton> tag. Notice that, in this case, we need to wrap the <h:commandButton> tag in a <h:form> tag, which is submitted using the POST request; therefore, the request parameters are not visible in the URL anymore.

Note

The <f:param> tag cannot be fortified with declarative/imperative validations and/or conversions. You need to accomplish this task by yourself.

Do not try to place the <f:param> tag inside the <h:inputText> tag or any other input component. That will simply not work.

Working with view parameters

Starting with JSF 2.0, we can use a new category of parameters, known as view parameters. These kinds of parameters are implemented by the UIViewParameter class (that extends the UIInput class) and are defined in Facelets using the <f:viewParam> tag. Through this tag, we can declaratively register the UIViewParameter class as metadata for the parent view; this is why the <f:viewParam> tag is nested in the <f:metadata> tag.

Starting with JSF 2.0, the metadata concept was materialized in a section of a view, which provides the following two main advantages (the section is demarcated by the <f:metadata> tag):

  • The content of this section is readable without having the entire view available
  • At the initial request, components from this section can accomplish different things before the view is rendered

Note

Starting with JSF 2.2, the metadata section (and subsequent components) is detected via a public static method, named the hasMetadata (UIViewRoot) method. This method was added in javax.faces.view.ViewMetadata and returns true if there is a metadata section and false otherwise. Among other benefits, the main advantage of using the <f:viewParam> tag is the URL bookmarking support.

For better understanding, let's look at a simple example of using the <f:viewParam> tag. The following pieces of code are from the same page, index.xhtml:

<f:metadata>
  <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/>
  <f:viewParam name="playersurnameparam"  value="#{playersBean.playerSurname}"/> 
</f:metadata>
...
<h:body>
  You requested name: <h:outputText value="#{playersBean.playerName}"/><br/>
  You requested surname: <h:outputText value="#{playersBean.playerSurname}"/>
</h:body>

Now, let's see what is happening at the initial request. First, let's focus on the first block of code: here, JSF gets the request parameter's values by their names (playernameparam and playersurnameparam) from the page URL and applies the specified converter/validators (these are optional). After conversion/validation succeeds, before the view is rendered, JSF binds the values of the playernameparam and playersurnameparam request parameters to the managed bean properties, playerName and playerSurname, by calling the setPlayerName and setPlayerSurname methods (called only if we provide request parameters in the URL). If the value attribute is missing, then JSF sets request parameters as request attributes on names, playernameparam and playersurnameparam, available via #{playernameparam} and #{playersurnameparam}.

The page's initial URL should be something like the following one:

http://hostname/ch2_5/?playernameparam=Rafael&playersurnameparam=Nadal

In the second block of code, the values of the managed bean properties, playerName and playerSurname, are displayed (the getPlayerName and getPlayerSurname methods are called); they should reflect the values of the request parameters.

Note

Since the UIViewParameter class extends the UIInput class, the managed bean properties are set during the Update Model phase only.

This example is wrapped into the application named ch2_5.

View parameters can be included in links (the GET query string) by using the includeViewParams="true" attribute in the <h:link> tag, or the includeViewParams=true request parameter in any URL. Both these cases can be seen in the upcoming examples.

In the index.xhtml file, you can have something like the following code, in which view parameters are included through the request parameter:

<f:metadata>
  <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/>
  <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> 
</f:metadata>
...
<h:body>        
  <h:form>
    Enter name:<h:inputText value="#{playersBean.playerName}"/>
    Enter name:<h:inputText value="#{playersBean.playerSurname}"/>
  <h:commandButton value="Submit" action="results?faces-redirect=true&amp;includeViewParams=true"/>
  </h:form>               
</h:body>

The initial URL can be:

http://hostname/ch2_6/?playernameparam=Rafael&playersurnameparam=Nadal

The view parameters, playernameparam and playersurnameparam, will be extracted from this URL and bound to the managed bean properties, playerName and playerSurname. Optionally, both properties can be further altered by the user through two <h:inputText> tags, or other UI components. (If the initial URL does not contain the view parameters, then the <h:inputText> generated fields will be empty.) The button rendered through the <h:commandButton> tag will redirect the flow to the results.xhtml page and will include the view parameters in the new URL. The values of the view parameters will reflect the values of the corresponding managed bean properties, since the form is submitted before the following URL is composed:

http://hostname/ch2_6/faces/results.xhtml?playernameparam=Rafael&playersurnameparam=Nadal

The results.xhtml file (or any other page that the index.xhtml file directs) will use the <f:viewParam> tag to take parameters from the GET request into bound properties, as shown in the following code:

<f:metadata>
  <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/>
  <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> 
</f:metadata>
...
<h:body>        
  You requested name: <h:outputTextvalue="#{playersBean.playerName}"/><br/>
  You requested surname: <h:outputText value="#{playersBean.playerSurname}"/>       
</h:body>

If you prefer to use a <h:link> tag in conjunction with the includeViewParams attribute set to true, then the index.xhtml file will be as follows (in this case, there is no form submission and no POST request):

<f:metadata>
  <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/>            
 <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> 
</f:metadata>
...
<h:body>        
  <h:link value="Send"outcome="results?faces-redirect=true" includeViewParams="true"/>
</h:body>

These examples are wrapped into the application named ch2_6.

You can use the includeViewParams request parameter in any URL, which means that you can use it in managed beans to include view parameters in the navigation links as follows:

<f:metadata>
  <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/>            
  <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> 
</f:metadata>
...
<h:body>
  <h:form>
    Enter name:<h:inputText value="#{playersBean.playerName}"/>
    Enter name:<h:inputText value="#{playersBean.playerSurname}"/>
    <h:commandButton value="Submit" action="#{playersBean.toUpperCase()}"/>
  </h:form>
</h:body>

And the action method is as follows:

public String toUpperCase(){
  playerName=playerName.toUpperCase();
  playerSurname=playerSurname.toUpperCase();
        
  return "results?faces-redirect=true&includeViewParams=true";
}

The complete application is named ch2_7 and is available in the code bundle of this chapter on the Packt Publishing website.

As you know from the previous code, the UIViewParameter class extends the UIInput class, which means that it inherits all attributes, such as required and requiredMessage. When the URL must contain view parameters, you can use these two attributes to ensure that the application flow is controlled and the user is correctly informed. The following is the example code:

<f:metadata>
  <f:viewParam name="playernameparam" required="true" requiredMessage="Player name required!" value="#{playersBean.playerName}"/>            
  <f:viewParam name="playersurnameparam" required="true" requiredMessage="Player surname required!" value="#{playersBean.playerSurname}"/> 
</f:metadata>

If the initial URL does not contain the view parameters (one or both), then you will receive a message that report this fact. This example is wrapped into the application named ch2_9.

Moreover, view parameters support fine-grained conversion and validation. You can use <f:validator> and <f:converter>, or the validator and converter attributes inherited from the UIInput class. Supposing that you have a custom validator, named PlayerValidator (its implementation is not really relevant), the following is its code:

@FacesValidator("playerValidator")
public class PlayerValidator implements Validator {

  @Override
  public void validate(FacesContext context, UIComponent component, 
  Object value) throws ValidatorException {
    //validation conditions
   ...

Then, you can attach it to a view parameter as shown in the following code:

<f:metadata>
  <f:viewParam id="nameId" name="playernameparam"  validator="playerValidator" value="#{playersBean.playerName}"/>            
  <f:viewParam id="surnameId" name="playersurnameparam" validator="playerValidator"value="#{playersBean.playerSurname}"/>         
</f:metadata>  

The preceding snippet of code accomplishes the following tasks:

  • Gets the request parameters' values by their names, playernameparam and playersurnameparam
  • Converts and validates (in this case, validates) parameters
  • If conversions and validations end successfully, then the parameters are set in managed bean properties
  • Any validation failure will result in a message being displayed

Note

For the customize messages style, you can attach a <h:message> tag to the <f:viewParam> tag.

This example is wrapped into the application named ch2_10.

Note

If you want to preserve the view parameters over validation failures, then you need to use a broader scope than @RequestScoped, such as @ViewScoped, or to manually preserve the request parameters for the subsequent requests through the <f:param> tag in the command components.

Sometimes, you may need a converter for a view parameter. For example, if you try to pass a java.util.Date parameter as a view parameter from a managed bean, you will probably will code it as follows:

private Date date = new Date();
...  
public String sendDate() {
  String dateAsString = new SimpleDateFormat("dd-MM-yyyy").format(date);
  return "date.xhtml?faces-redirect=true&date=" + dateAsString;
}

Now, in the date.xhtml file, you need to convert the view parameter from string to date, and for this, you may use the <f:convertDateTime> converter, as shown in the following code:

<f:viewParam name="date" value="#{dateBean.date}">
  <f:convertDateTime pattern="dd-MM-yyyy" />
</f:viewParam>

Of course, a custom converter can also be used. The complete application is named ch2_29.

Among so many advantages of using the <f:viewParam> tag, we have a gap. When view parameters are set in managed bean properties, the set values are not available in @PostConstruct; therefore, you cannot perform initialization or preload tasks directly. You can quickly fix this by attaching the preRenderView event listener, as shown in the following code:

<f:metadata>
  <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/>            
  <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> 
 <f:event type="preRenderView" listener="#{playersBean.init()}"/>
</f:metadata>

The init method is shown as follows:

public void init() {        
  // do something with playerName and playerSurname 
}

Note

The set values are not available in @PostConstruct when using the <f:viewParam> tag. You can fix this by attaching the preRenderView event listener, or, as you will see next, the <f:viewAction> tag.

This example is wrapped into the application named ch2_8.

Well, there is one more aspect that I'd like to discuss here. The UIViewParameter class (<f:viewParam>) is a stateful component that stores its value in state. This is very nice as the value is available over postbacks, even if it doesn't come from the page URL anymore or the managed bean is request scoped. So, you need to indicate view parameters only once, and not for every request. But, there are a few drawbacks of this behavior—the most significant being calling the setter method at each postback (you don't want this in view beans). Another one is calling, for each postback, the method indicated through the preRenderView event handler; this can be fixed using a test as shown in the following code. The complete application is named ch2_28.

public void init() {        
  if (!FacesContext.getCurrentInstance().isPostback()) {
    // do something with playerName and playerSurname 
  }
}

Maybe the most painful drawback is converting and validating view parameters at each postback. Obviously, this is not the behavior you are expecting to see. In order to call a converter/validator only when the page URL contains the request parameters, you need to alter the UIViewParameter class implementation by writing a custom implementation. You can try to write a stateless UIViewParameter class or to control the conversion/validation calls. Of course, you have to keep in mind that altering the default implementation may lead to more or less unpredictable drawbacks. As an alternative, you can use the <o:viewParam> tag from OmniFaces, which fixes these issues. A relevant example can be seen at http://showcase.omnifaces.org/components/viewParam.

So, as a final conclusion of this section, the <f:viewParam> tag is used to capture the request parameters. Moreover, it can be used with the <h:link> and <h:button> tags to send outgoing request parameters, or in non-JSF forms, to send data to JSF pages that use the <f:viewParam> tag, or to make JSF results pages bookmarkable in a POST-redirect-GET flow. On the other hand, the <f:viewParam> tag doesn't sustain the <h:form> tag to use GET or provide access to random JSF pages via the GET request.

Calling actions on GET requests

Starting with JSF 2.2, we can deal with calling actions on GET requests by using the new generic view action feature (well-known in Seam 2 and 3). This new feature is materialized in the <f:viewAction> tag, which is declared as a child of the metadata facet, <f:metadata>. This allows the view action to be part of the JSF life cycle for faces/non-faces requests.

In the preceding section, we saw how to attach a custom validator to a <f:viewParam> tag for validating view parameters. The same thing can be accomplished using the <f:viewAction> tag, when the validation method is declared in the managed bean instead of being a separate implementation of the Validator interface. For example, in the index.xhtml file, you may have the following code:

<f:metadata>
  <f:viewParam id="nameId" name="playernameparam" value="#{playersBean.playerName}"/>            
  <f:viewParam id="surnameId" name="playersurnameparam" value="#{playersBean.playerSurname}"/>   
 <f:viewAction action="#{playersBean.validateData()}"/>
</f:metadata>

As you can see, the following validateData method is just a common method declared in PlayersBean:

public String validateData() {
  //validation conditions
  return "index"; //or other page
}

This example is wrapped into the application named ch2_11.

Note

The <f:viewAction> tag and the preRenderView event listener are not the same!

The preceding note underlines our next discussion. You may think that they are the same because in the preceding example, you can replace <f:viewAction> with preRenderView and obtain the same effect (result). Well, it is true that they are partially the same, but a few existing differences are important, as you can see in the following four bullets:

  • By default, the preRenderView event listener is executed on postback requests, while the view action is not. In the case of the preRenderView event listener, you need to overcome this by testing the request type as follows:
    if (!FacesContext.getCurrentInstance().isPostback()) {
      // code that should not be executed in postback phase
    }

    For example, the following code will try to apply some modifications over the set values using the preRenderView event listener:

    <f:metadata>
      <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/>
      <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> 
      <f:event type="preRenderView" listener="#{playersBean.init()}"/>
    </f:metadata>

    The init method is declared in PlayersBean and it just turns the set values to uppercase, as shown in the following code:

    public void init() {
      if (playerName != null) {
        playerName = playerName.toUpperCase();
      }
      if (playerSurname != null) {
        playerSurname = playerSurname.toUpperCase();
      }
    }

    Next, when the JSF page is rendered, the set values are used in uppercase, and further requests can be accomplished (for example, you may want to call the method #{playersBean.userAction()} when a certain button is clicked). But, each further request will call the init method again (after the userAction method), because the preRenderView event listener is executed at postback time. Except for the case when this is the desired functionality, you need to programmatically test the postbacks to prevent the following init method code from being executed:

    public void init() {
      if (!FacesContext.getCurrentInstance().isPostback()) {
        if (playerName != null) {
          playerName = playerName.toUpperCase();
        }
        if (playerSurname != null) {
          playerSurname = playerSurname.toUpperCase();
        }
       }
    }

    Well, this is not the same in the case of the <f:viewAction> tag. Replace the preRenderView event listener with the <f:viewAction> tag, as shown in the following code:

    <f:metadata>
      <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/>
      <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> 
      <f:viewAction action="#{playersBean.init()}"/>
    </f:metadata>

    The <f:viewAction> tag supports an attribute named onPostback which is set to false by default, meaning that the init method will not be called on postback requests. Of course, if you set it to true, then it will function contrary; but, notice that in the case of the preRenderView event listener, the init method is called after the userAction method, while in the case of the <f:viewAction> tag, the init method is called before the userAction method, as shown in the following line of code:

    <f:viewAction action="#{playersBean.init()}" onPostback="true"/>

    The example based on the preRenderView event listener is wrapped in the application named ch_12_1, while for the <f:viewAction> tag it is named ch_12_2.

  • The view action has navigation capabilities, while the preRenderView event listener doesn't. While the view action can naturally accomplish navigation tasks, the preRenderView event listener requires explicit navigation based on the JSF API.

    For example, if you modify the preceding init method to return the start.xhtml view, then you will probably change it as shown in the following code:

    public String init() {
      if (playerName != null) {
        playerName = playerName.toUpperCase();
      }
      if (playerSurname != null) {
        playerSurname = playerSurname.toUpperCase();
      }
      return "start";
    }

    But, this will not work with the preRenderView event listener! You will need to add explicit navigation by returning void and replacing the return "start" code line with the following code:

    ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) FacesContext.getCurrentInstance().getApplication().getNavigationHandler();
    handler.performNavigation("start");

    If you drop the preRenderView event listener and use the <f:viewAction> tag instead, then the preceding init method will correctly navigate to start.xhtml without involving an explicit call of the navigation handler.

    The example based on the preRenderView event listener is wrapped in the application named ch_13_1, while for the <f:viewAction> tag it is named ch_13_2.

    Moreover, the <f:viewAction> tag supports declarative navigation. So, you can write a navigation rule in the faces-config.xml file that is consulted before the page is rendered. For example:

    <navigation-rule>
      <from-view-id>index.xhtml</from-view-id>
      <navigation-case>
        <from-action>#{playersBean.init()}</from-action> 
        <from-outcome>start</from-outcome>
        <to-view-id>rafa.xhtml</to-view-id>
        <redirect/>
      </navigation-case>
    </navigation-rule>

    Now, the rafa.xhtml page will be rendered instead of the start.xhtml page. This example is wrapped into the application named ch2_13_3.

  • By default, the view action is executed in the Invoke Application phase. But, it can be executed in the Apply Request Values phase by setting the immediate attribute to true, as shown in the following code:
    <f:viewAction action="#{playersBean.init()}" immediate="true"/>
  • Moreover, you can specify in which phase to execute the action using the phase attribute whose value represents the phase name as a predefined constant. For example:
    <f:viewAction action="#{playersBean.init()}" phase="UPDATE_MODEL_VALUES"/>

    The supported values are APPLY_REQUEST_VALUES, INVOKE_APPLICATION, PROCESS_VALIDATIONS, and UPDATE_MODEL_VALUES.

Note

The view action can be placed into a view metadata facet that doesn't contain other view parameters.

Passing attributes with the <f:attribute> tag

When the <f:param> tag does not satisfy your needs, maybe the <f:attribute> tag will. This tag allows you to pass the value of an attribute of a component or to pass a parameter to a component.

For example, you can assign the value of the attribute named value of a <h:commandButton> tag as shown in the following code:

<h:commandButton actionListener="#{playersBean.parametersAction}">
  <f:attribute name="value" value="Send Rafael Nadal" />
</h:commandButton>

This will render a button labeled Send Rafael Nadal. Its code is given as follows:

<h:commandButton value="Send Rafael Nadal" actionListener="#{playersBean.parametersAction}">

Moreover, the <f:attribute> tag can be used to pass a parameter to a component, as shown in the following code:

<h:commandButton actionListener="#{playersBean.parametersAction}">
  <f:attribute id="playerName" name="playerNameAttr" value="Rafael"/>               
  <f:attribute id="playerSurname" name="playerSurnameAttr" value="Nadal"/>
</h:commandButton>

In the action listener method, you can extract the attributes' values as shown in the following code:

private final static Logger logger = Logger.getLogger(PlayersBean.class.getName());
private String playerName;
private String playerSurname;
...
//getters and setters
...
public void parametersAction(ActionEvent evt) {     

  playerName = (String) evt.getComponent().getAttributes().get("playerNameAttr");
  playerSurname = (String) evt.getComponent().getAttributes().get("playerSurnameAttr");

  logger.log(Level.INFO, "Name: {0} Surname: {1}", new Object[]{playerName, playerSurname});
}

This example is wrapped into the application named ch2_14.

If you are a fan of PrimeFaces ( pass some extra parameters, for example, the files' owner name and surname. Well, the <p:fileUpload> tag doesn't come with a solution for this, but the <f:attribute> tag can be helpful. The following is the code of a classic <p:fileUpload> tag with the <f:attribute> tag:

<h:form>  
  <p:fileUpload 
    fileUploadListener="#{fileUploadController.handleFileUpload}" 
    mode="advanced" dragDropSupport="false"  
    update="messages" sizeLimit="100000" fileLimit="3"   
    allowTypes="/(\.|\/)(gif|jpe?g|png)$/">  
  <f:attribute id="playerName" name="playerNameAttr" value="Rafael"/>               
  <f:attribute id="playerSurname" name="playerSurnameAttr" value="Nadal"/> 
  </p:fileUpload>
  <p:growl id="messages" showDetail="true"/>  
</h:form>

The handleFileUpload method is responsible for the upload-specific steps (skipped in the following code), but it can also access the values passed by the <f:attribute> tag:

public void handleFileUpload(FileUploadEvent evt) {

  //upload specific tasks, see PrimeFaces documentation

  String playerName = (String) evt.getComponent().getAttributes().get("playerNameAttr");
  String playerSurname = (String) evt.getComponent().getAttributes().get("playerSurnameAttr");

  FacesMessage msg = new FacesMessage("Successful", evt.getFile().getFileName() + " is uploaded for " + playerName + " " + playerSurname);

  FacesContext.getCurrentInstance().addMessage(null, msg);
}

If you are not a fan of PrimeFaces, then you might probably think that this example is useless, but maybe you are a fan of some other third-party library, such as RichFaces, ICEFaces, and MyFaces. You can apply this technique for other component libraries as well.

This example is wrapped into the application named ch2_15.

Another case when the <f:attribute> tag can be useful is when dynamically passing parameters in conjunction with UI components bound to the managed bean using the binding attribute. This is very useful, especially because there is no solution provided by JSF for passing parameters to the getters/setters methods of the bound UI components, as shown in the following code:

<h:form>
  <h:inputText binding="#{playersBean.htmlInputText}" value="#{playersBean.playerNameSurname}">
    <f:attribute name="playerNameAttr" value="Rafael Nadal"/>
  </h:inputText>
</h:form>

Note

Now, the value of the <h:inputText> tag should contain the value set via the <f:attribute> tag. Be careful to use only unique names for the attributes and to not interfere (try to overwrite) with the default attributes of the UI component.

Also, the PlayersBean managed bean's code is as follows:

@Named
@RequestScoped
public class PlayersBean {

  private UIInput htmlInputText= null;    
  
  public PlayersBean() {
  }

  public UIInput getHtmlInputText() {
    return htmlInputText;
  }

  public void setHtmlInputText(UIInput htmlInputText) {
    this.htmlInputText = htmlInputText;
  }    
    
  public String getPlayerNameSurname() {
 return (String) htmlInputText.getAttributes().get("playerNameAttr");
  }    
}

As you can see, all the parameters passed this way are accessible via the getAttributes method of the parent UI component.

This example is wrapped into the application named ch2_23.

Setting property values via action listeners

The <f:setPropertyActionListener> tag uses an action listener (created by the framework) to directly set a value into a managed bean property; it is placed within a component derived from the ActionSource class. The target attribute indicates the managed bean property, while the value attribute indicates the value of the property, as shown in the following code:

<h:commandButton value="Send Rafael Nadal 1">
  <f:setPropertyActionListener id="playerName"  target="#{playersBean.playerName}" value="Rafael"/>
  <f:setPropertyActionListener  id="playerSurname" target="#{playersBean.playerSurname}" value="Nadal"/>
</h:commandButton>

Now, in the PlayersBean managed bean, the setter methods are called and the values are set; logger is useful to see the application flow and to understand how listeners are fired, as shown in the following code:

private final static Logger logger =Logger.getLogger(PlayersBean.class.getName());
private String playerName;
private String playerSurname;

public void setPlayerName(String playerName) {
  this.playerName = playerName;
  logger.log(Level.INFO, "Player name (from setPlayerName() method: {0}", playerName);
}

public void setPlayerSurname(String playerSurname) {
  this.playerSurname = playerSurname;
  logger.log(Level.INFO, "Player surname (from setPlayerSurname() method: {0}", playerSurname);
}

When the button labeled Send Rafael Nadal 1 is clicked, the application output will be as follows:

INFO:   Player name (from setPlayerName() method: Rafael
INFO:   Player surname (from setPlayerSurname() method: Nadal

Note

Keep in mind that action listeners are executed in the order they are defined, which means that the presence of the <f:setPropertyActionListener> tag can affect the order in which the listeners are fired.

This note is important! For a clear understanding, take a look at the following snippet of code:

<h:commandButton value="Send Rafael Nadal 2" actionListener="#{playersBean.parametersAction}">
  <f:setPropertyActionListener id="playerName" target="#{playersBean.playerName}" value="Rafael"/>
  <f:setPropertyActionListener  id="playerSurname" target="#{playersBean.playerSurname}" value="Nadal"/>
</h:commandButton>

The following code is of the parametersAction method:

public void parametersAction(ActionEvent e) {        
  logger.log(Level.INFO, "Player name (from parametersAction(ActionEvent) method: {0}", playerName);
  logger.log(Level.INFO, "Player surname (from parametersAction(ActionEvent) method: {0}", playerSurname);
}

Well, this code does not work as expected! Probably, you think that the setters method is called first and the parametersAction method later; therefore, the set values are available in the action method. But, the following output will prove the opposite:

INFO:   Player name (from parametersAction() method: null
INFO:   Player surname (from parametersAction() method: null
INFO:   Player name (from setPlayerName() method: Rafael
INFO:   Player surname (from setPlayerSurname() method: Nadal

So, the properties are set after the command action listener is fired! To fix this issue, you can use the action attribute instead of actionListener:

<h:commandButton value="Send Rafael Nadal 3" action="#{playersBean.parametersAction()}">
  <f:setPropertyActionListener id="playerName" target="#{playersBean.playerName}" value="Rafael"/>
  <f:setPropertyActionListener  id="playerSurname" target="#{playersBean.playerSurname}" value="Nadal"/>
</h:commandButton>

Of course, you need to adjust the parametersAction method accordingly, as shown in the following code:

public void parametersAction() {        
  logger.log(Level.INFO, "Player name (from parametersAction() method: {0}", playerName);
  logger.log(Level.INFO, "Player surname (from parametersAction() method: {0}", playerSurname);
}

Now, the output will reflect the following desired result:

INFO:   Player name (from setPlayerName() method: Rafael
INFO:   Player surname (from setPlayerSurname() method: Nadal
INFO:   Player name (from parametersAction() method: Rafael
INFO:   Player surname (from parametersAction() method: Nadal

This example is wrapped into the application named ch2_16.

Passing parameters using the Flash scope

The new JSF Flash scope is a very handy tool when you need to pass parameters between user views without the need to store them in the session. The Flash scope is simple to understand if you keep in mind that variables stored in the Flash scope will be available over a redirection and they will be eliminated afterwards. This is really useful when implementing a POST-redirect-GET pattern.

For a better understanding, let's suppose the following scenario:

  • A player (user) needs to register on the ATP website. Among other information, he will provide his name and surname and click on the Register button. This is accomplished in the index.xhtml page.
  • The application flow redirects the player to the page terms.xhtml. On this page, the user can see a welcome message containing his name and surname and some terms and conditions that must be accepted (using the Accept button) or rejected (using the Reject button).
  • If the Reject button is clicked, then the user is redirected to the index.xhtml home page, and the form registration fields will reveal the information provided by him earlier. Moreover, he will see a generated message stating Terms rejected! Player not registered!. This is outputted by the <h:message> tag.
  • If the Accept button is clicked, then the user is redirected to a page named done.xhtml. On this page, the user will see a generated message stating Terms accepted and player registered! and another message stating Name Surname successfully registered!. The first message is outputted by the <h:message> tag, while the second one by the <h:outputText> tag.

The following is a screenshot of both the scenarios:

Obviously, you can implement this flow only if you store the submitted values somewhere, because they will not survive during the redirect process. This means that using a managed bean in the request scope cannot be a valid option. But, if we add in discussion the new Flash scope, then things become more favorable for the request scoped bean.

It will be much easier to follow this idea if you take a quick look at the following code of the request scoped bean, named PlayersBean:

@Named
@RequestScoped
public class PlayersBean {

  private final static Logger logger = Logger.getLogger(PlayersBean.class.getName());
  private String playerName;
  private String playerSurname;

...

  public String addValuesToFlashAction() {

    Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
    flash.put("playerName", playerName);
    flash.put("playerSurname", playerSurname);

    return "terms?faces-redirect=true";
  }

  public void pullValuesFromFlashAction(ComponentSystemEvent e) {

    Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
    playerName = (String) flash.get("playerName");
    playerSurname = (String) flash.get("playerSurname");
  }

  public String termsAcceptedAction() {

    Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();

    flash.setKeepMessages(true);
    pullValuesFromFlashAction(null);

    //do something with firstName, lastName 
    logger.log(Level.INFO, "First name: {0}", playerName);
    logger.log(Level.INFO, "Last name: {0}", playerSurname);

    FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Terms accepted and player registered!"));
    return "done?faces-redirect=true";
  }

  public String termsRejectedAction() {

    Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();

    flash.setKeepMessages(true);
    pullValuesFromFlashAction(null);

    FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Terms rejected! Player not registered!"));
    return "index?faces-redirect=true";
  }
}

Also, take a look at the start page, index.xhtml. Its code is as follows:

  <h:body>
    <f:metadata> 
      <f:event type="preRenderView" listener="#{playersBean.pullValuesFromFlashAction}"/> 
    </f:metadata>
    <h:messages />  
    <h:form>                       
      Name: <h:inputText value="#{playersBean.playerName}"/>
      Surname: <h:inputText value="#{playersBean.playerSurname}"/>
     <h:commandButton value="Register" action="#{playersBean.addValuesToFlashAction()}"/>          
    </h:form>
  </h:body>

So, the submission process begins when the user clicks on the button labeled Register. JSF will call the addValuesToFlashAction method, which is responsible for putting the submitted values to the Flash scope; this will ensure that the values will survive during redirect to the terms.xhtml page.

If the user rejects the terms and conditions, then he is redirected to the index.xhtml page. Here, you need to repopulate the registration form fields with the user-inserted values. For this, you can use the preRenderView event, which will load the values from the Flash scope during the render response phase by calling the pullValuesFromFlashAction method.

Next, let's focus on the terms.xhtml page; its code is as follows:

  <h:body>
    <h:messages />  
      Hello, <h:outputText value="#{flash.keep.playerName} #{flash.keep.playerSurname}"/>    
    <br/><br/>Terms &amp; Conditions ... ... ... ... ...
    <h:form>
    <h:commandButton value="Reject" action="#{playersBean.termsRejectedAction()}" />
    <h:commandButton value="Accept" action="#{playersBean.termsAcceptedAction()}" />
    </h:form>
  </h:body>

First, this page displays the entered values wrapped into a welcome message. The values are obtained from the Flash scope using the following code:

#{flash.keep.playerName}
#{flash.keep.playerSurname}

Notice that this approach has two functions, which are listed as follows:

  • It obtains the values from the Flash scope, which could also be accomplished with the following lines:
    #{flash.playerName}
    #{flash.playerSurname}
  • It tells JSF to keep the values in the Flash scope for the next request. This is needed because values put to the Flash scope survive only one redirect and then are deleted. We have already fired a redirect when we have navigated from the index.xhtml page to the terms.xhtml page. But, another redirect will appear when the Accept or Reject button is clicked.

Note

Values stored in the Flash scope survive only one redirect and then are deleted.

Furthermore, the page displays both the buttons for navigating back to the index.xhtml page and forward to the done.xhtml page. The Accept button will call the termsAcceptedAction method, which will basically preserve messages across redirects (it calls the setKeepMessages method) and redirects the flow to the done.xhtml page. In the same manner, the Reject button calls the termsRejectedAction method, preserves messages in the Flash scope, and redirects the flow to the index.xhtml page.

The done.xhtml page is presented using the following code:

  <h:body>
    <f:metadata> 
     <f:event type="preRenderView" listener="#{playersBean.pullValuesFromFlashAction}"/> 
    </f:metadata>
    <h:messages />  
    <h:outputText value="#{playersBean.playerName} #{playersBean.playerSurname}"/> successfully registered!
  </h:body>

The preRenderView event listener is used again for obtaining the values from the Flash scope.

This example is wrapped into the application named ch2_21.

Replacing the <f:param> tag with the JSTL <c:set> tag

Sometimes, the JSTL <c:set> tag can solve issues that the JSF <f:param> tag can't. Probably, you know that we can pass parameters to the <ui:include> tag using the <f:param> tag, as shown in the following code:

<ui:include src="rafa.xhtml">
  <f:param name="rafa" value="Rafael Nadal Page"/>,
</ui:include>

Well, this approach triggers an issue! Now, the Rafael Nadal Page value will be available in the included page through EL, #{rafa}, but will not be available in the constructor of the managed bean of the included page!

It is time for the <c:set> tag to save the situation; therefore, the code will be changed to the following:

<ui:include src="rafa.xhtml">
  <c:set var="rafa" value="Rafael Nadal Page" scope="request"/>,
</ui:include>

Done! Now, in the constructor of the managed bean, the value can be extracted as shown in the following code:

public ConstructorMethod(){
  FacesContext facesContext = FacesContext.getCurrentInstance();
  HttpServletRequest httpServletRequest  = (HttpServletRequest) facesContext.getExternalContext().getRequest();
  String rafa = (String) request.getAttribute("rafa");
}

In the Configuring system event listeners section in will see how to work with system events dedicated to the Flash scope.

Sending data through cookies

JSF provides a request cookie map that can be used to work with HTTP cookies. Setting cookies can be easily accomplished through JavaScript; the following are just some helper methods:

  • The JavaScript method for setting a cookie is as follows:
    function setCookie(cookie_name, value, expiration_days)
    {
      var expiration_date = new Date();
      expiration_date.setDate(expiration_date.getDate() + expiration_days);
      var c_value = escape(value) + ((expiration_days == null) ? "" : "; 
      expires=" + expiration_date.toUTCString());
      document.cookie = cookie_name + "=" + c_value;
    }

    The JavaScript method for deleting a cookie by the name is as follows:

    function deleteCookie(cookie_name) {
      document.cookie = encodeURIComponent(cookie_name) + "=deleted; expires=" + new Date(0).toUTCString();
    }
  • The JavaScript method for extracting a cookie by the name is as follows:
    function getCookie(cookie_name) {
      var i, part_1, part_2;
      var cookieslist = document.cookie.split(";");
      //<![CDATA[
      for (i = 0; i < cookieslist.length; i++)
      {
        part_1 = cookieslist[i].substr(0, cookieslist[i].indexOf("="));
        part_2 = cookieslist[i].substr(cookieslist[i].indexOf("=") + 1);
        part_1 = part_1.replace(/^\s+|\s+$/g, "");
        if (part_1 == cookie_name)
        {
          return unescape(part_2);
        }
      }
      //]]>
      return "nocookie";
    }

Let's suppose that you have two cookies named name and surname, as shown in the following code:

setCookie('name', 'Rafael', 1);
setCookie('surname', 'Nadal', 1);

JSF can access these cookies through the following request cookie map:

Object name_cookie = FacesContext.getCurrentInstance().getExternalContext().getRequestCookieMap().get("name");
Object surname_cookie = FacesContext.getCurrentInstance().getExternalContext().getRequestCookieMap().get("surname");

//set playerName property 
if (name_cookie != null) {
  playerName = (((Cookie) name_cookie).getValue());
}

//set playerSurname property 
if (surname_cookie != null) {
  playerSurname = (((Cookie) surname_cookie).getValue());
}

JSF also provides several getters and setters methods for working with cookies. These methods are given in the following table:

This example is wrapped into the application named ch2_18 and can be found in the code bundle of this chapter.

Working with hidden fields

Hidden fields can sometimes be very useful! Passing data in a subtle manner can be the perfect choice for dealing with temporary data or information provided by the user that should be used again and again. JSF offers the <h:inputHidden> tag to pass hidden parameters. The following code passes two hidden parameters to a managed bean:

<h:form id="hiddenFormId">
  <h:commandButton value="Send Rafael Nadal" onclick="setHiddenValues();" action="#{playersBean.parametersAction()}"/>
  <h:inputHidden id="playerName" value="#{playersBean.playerName}"/>
 <h:inputHidden id="playerSurname" value="#{playersBean.playerSurname}"/>
</h:form>

Usually, setting hidden field values from JavaScript is a common practice. When the button Send Rafael Nadal is clicked, the JavaScript function named setHiddenValues is called; this happens before the form submission. The setHiddenValues function is given in the following code:

<script type="text/javascript">
  function setHiddenValues() {
    document.getElementById('hiddenFormId:playerName').value = "Rafael";
    document.getElementById('hiddenFormId:playerSurname').value = "Nadal";
  }
</script>

Next, the hidden parameters are set in the indicated managed bean properties and the parametersAction method is called—the set values are ready to use!

This example is wrapped into the application named ch2_17 and can be found in the code bundle of this chapter.

Sending passwords

JSF provides a dedicated tag named <h:inputSecret> for rendering the following well-known HTML code:

<input type="password">

For example, you can use it as shown in the following code:

<h:form>
  <h:inputSecret value="#{playersBean.playerPassword}"/>
  <h:commandButton value="Send Password" action="#{playersBean.passwordAction()}"/>
</h:form>

This example is wrapped into the application named ch2_19.

Accessing UI component attributes programmatically

Accessing UI component attributes from managed beans using the JSF API is not a common approach, but sometimes you may find it useful. For example, let's suppose that we have the following form:

<h:form id="playerFormId">
  <h:inputText id="playerNameId" value="#{playersBean.playerName}"/> 
  <h:inputText id="playerSurnameId" value="#{playersBean.playerSurname}"/>
  <h:commandButton value="Process" action="#{playersBean.processAction()}"/>
</h:form>

Now, you want to obtain the values of the components with IDs, playerNameId and playerSurnameId, in the processAction method. Moreover, you want to set the value of the component with the ID, playerNameId, as RAFAEL. Programmatically (using the JSF API), you can achieve this as follows:

public void processAction() {

  UIViewRoot view = FacesContext.getCurrentInstance().getViewRoot();
  UIComponent uinc = view.findComponent("playerFormId:playerNameId");
  Object prev = ((UIInput) uinc).getAttributes().put("value", "RAFAEL");


  UIComponent uisc = view.findComponent("playerFormId:playerSurnameId");
  Object current = ((UIInput) uisc).getAttributes().get("value");
}

First, you need to obtain access to UIViewRoot, which is the top level UI component—the root of the UIComponent tree. Then, you can search by the ID for the desired UI component through the UI components tree using the findComponent method. Each UI component provides the getAttributes method, which can be used to gain access to the UI component attributes by their names. At this point, you can extract an attribute value using the get method, or set a new attribute value using the put method.

This example is wrapped into the application named ch2_20.

Passing parameters via method expressions

Passing parameters using method expressions is an elegant solution to send parameters as arguments to an action method of a managed bean. For example, let's focus on the following snippet of code:

<h:form>
 <h:commandButton value="Send Rafael Nadal"  action="#{playersBean.parametersAction('Rafael','Nadal')}"/>
</h:form>

As you can see in the following code, the action attribute indicates a method that gets two arguments:

private String playerName;
private String playerSurname;

//getters and setters

public String parametersAction(String playerNameArg, String playerSurnameArg) {       
       
  playerName = playerNameArg;
  playerSurname = playerSurnameArg;
        
  return "result";
}

In the same manner, you can pass numeric values or objects.

This example is wrapped into the application named ch2_26.

Communicating via the binding attribute

JSF UI components support an attribute named binding, which is rarely used and, sometimes, poorly understood. The story behind its meaning can be stretched over several pages or summed up in some golden rules. We will start with the binding lifespan and a brief overview and will end with the important rules that should be taken into account when you decide to used it in production.

If we want to localize the moment in time when the binding attribute enters the fray, we can refer to the moment when the JSF view is built or restored; the result of building/restoring the view is present in the component tree. So, before the component tree is deliverable, JSF needs to inspect all binding attributes. For each of them, JSF will check the presence of a pre-existing (precreated) component. If a pre-existing component is found, then it is used; otherwise, JSF will automatically create a brand new one, and will pass it as an argument to the setter method that corresponds to that binding attribute. In addition, JSF adds a reference of the component in the view state. Furthermore, a postback request (a form submit) will tell JSF to restore the view, which will restore the components and bindings based on view state.

Now that you know what happens with the binding attribute, let's enumerate some important aspects of using it:

  • After each request (initial or postback), JSF creates an instance of the component indicated by the binding attribute.
  • At the restore view (at the postback), after the component instance is created, JSF populates it from the view state, based on the stored reference.
  • When you bind a component to a bean property (of type UIComponent), you actually bind the whole component. This kind of binding is a very rare use case, and it may be useful when you want to work/expose a component's methods that are not available in the view or you need to alter the component's children in a programmatic fashion. Moreover, you can alter the component's attributes and instantiate the component rather than letting the page author do so.
  • Since JSF instantiates the component at each request, the bean must be in the request scope; otherwise, the component may be shared between different views. The view scope may also be a solution.
  • The binding attribute is also used to bind the component to the current view, without the need of a bean. This is useful to gain access to the state of a component from another component.
  • Binding a component without a bean property will put the component in the EL scope. This happens when the component tree is built; therefore, EL is perfectly capable to reveal the bound component at the rendering stage, which takes place after the component tree was built.

For example, a <h:dataTable> tag has three useful properties: first, rows, and rowCount. If you bind a <h:dataTable> tag to the current view, then outside of this component, you can access these properties as shown in the following line of code:

<h:dataTable value="#{playersBean.dataArrayList}" binding="#{table}" var="t">

For example, you can set the rows property as follows:

#{table.rows = 3;''}

Also, display the rowCount and first properties as follows:

<h:outputText value="#{table.rowCount}"/>
<h:outputText value="#{table.first}"/>

The complete application is named ch2_32.

We can accomplish the same thing from a bean. First, we bind the <h:dataTable> tag to a bean property of type HtmlDataTable as follows:

<h:dataTable value="#{playersBean.dataArrayList}" binding="#{playersBean.table}" var="t">

Now, in PlayersBean, we add the following code:

private HtmlDataTable table;
...
//getter and setter
...
public void tableAction() {
  logger.log(Level.INFO, "First:{0}", table.getFirst());
  logger.log(Level.INFO, "Row count: {0}", table.getRowCount());
  table.setRows(3);
}

The complete application is named ch2_31.