Welcome!

APIs for the Internet of Things

Max Katz

Subscribe to Max Katz: eMailAlertsEmail Alerts
Get Max Katz via: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Blog Feed Post

Learning JSF2: Page params and page actions

Learning JSF 2 series continues with page parameters and page actions. Read other Learning JSF 2 articles.

If you worked with JSF 1.2, you know that calling a page action wasn’t simple. A page action is useful when you request a page for the first time (GET request) and would like to prepare some data to be rendered on a view (page). Of course there are some workarounds, one of them is to use @PostConstruct method inside the bean or even create a phase listener. Both could work but developers desired a more robust, and out of the box solution similar to what Seam 2 provides.

A feature that closely relates to page action is page params (page parameters). Usually when you issue a get request like this:

http://host/app/page.jsf?productId=101

you want the productId request parameter to be assigned to a property inside a bean so that you can use the id inside action (page action) to load data to display on the page. In JSF 1.2, you would end up doing something like this:

Managed bean:

@ManagedBean(name="bean")
public class Bean {
   private Integer productId;  // getter and setter
}

JSF configuration file:

<managed-bean>
  <managed-bean-name>bean</managed-bean-name>
  <managed-bean-class>test.Bean</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <managed-property>
   <property-name>productId</property-name>
   <value>#{param['productId']}</value>
  </managed-property>
</managed-bean>

#{param} is an implicit object in JSF that holds all request parameters. In the code snippet above, we are taking the value of productId and assigning it to productId property inside the bean using JSF managed bean facility. It’s just not an elegant solution and it doesn’t offer any flexibility, ability to validate the value, and also defined separately from the actual page.

The good news is that both page actions and page params are supported by JSF 2. Let’s start with page params.

Page params

We all know that when we have a JSF input field bound to a bean property and we submit the page ( POST), the value entered in the page automatically get pushed into the bean (assuming no conversion/validation errors or anything else stopping the life cycle).

JSF page:

<h:inputText value="#{bean.productId}" />

Bean:

public class Bean {
   private Integer productId;  // getter and setter
}

The easiest way to think of page params is that the same thing is happening as above but only without input fields on the page. Instead, the values are submitted via the GET URL and pushed in the same way into the bean:

http://host/app/page.jsf?productId=101

In both cases the result is the same, the value either from the page or URL is set into the property. We know how to do it with input field bound to a property during a POST. Let’s see what needs to be done when a GET request is issued. We have to somehow map the request param to bean properties.

JSF 2 introduced a new metadata tag called inside which pages params are defined:

<!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://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ui="http://java.sun.com/jsf/facelets">
 
<h:head></h:head>
 
<f:metadata>
  <f:viewParam name="productId" value="#{bean.productId}"/> 	
  <f:viewParam name="text" value="#{bean.text}"/>
</f:metadata>
 
<body>
  ...
</body>
</html>

In the code example above tag contains two new tags in JSF 2. As you guessed, the tag defines and maps two page params to bean properties. In the first one, productId param is mapped to bean.productId property. The second param text is mapped to bean.text bean property. The following GET request will take productId value of 101 and text value of ‘nice’ and set them into their respective bean properties:

http://localhost:port/app/page.jsf?productId=111&text=nice

A quick and easy way to test that values were set into the proprieties is just to render the values on the page:

<h:outputText value="ProductId: #{bean.productId}" />
<h:outputText value="Text: #{bean.text}" />

Default values

If bean properties are set to some initial values page params will overwrite them (same as with a POST). If page params are not provided then during rendering the default values will be displayed.

Good to know

The tag is actually an alias for a special view level facet in JSF 2:

<f:view>
   <f:facet name="javax_faces_metadata" ></f:facet>
</f:view>

As this metadata tag going to be used extensively, a shortcut tag (alias) was created:

<f:view>
   <f:metadata></f:metadata>
</f:view>

The tag is actually not required. As we are using Facelets (default in JSF 2), the entire page is a view by default.

Conversation and validation

When a value is submitted via a POST it can bean converted and validated before being pushed into the bean property. For a GET request, conversation and validation can be invoked in a similar fashion. When either fails, error message can be displayed just like with a POST.

When components are bound to a bean property, a converted or validator is registered with the component on the page. As we don’t have a component, conversation and validation is added to the tag.

Requiring a value

To make a value required, we use the familiar required attribute and overwrite the default error message:

<f:metadata>
   <f:viewParam name="productId" value="#{bean.productId}" required="true"
		requiredMessage="Product id is required"/>
   <f:viewParam name="text" value="#{bean.text}" required="true"
		requiredMessage="Text is required"/>
</f:metadata>

To display the error message, is added to the page:

<h:messages />
<h:outputText value="ProductId: #{bean.productId}" />
<h:outputText value="Text: #{bean.text}" />

Note: we have to use as the message is not associated with any component.

For example, the following request:

http://localhost:port/app/page.jsf?productId=111&text=

will produce the following:

As we didn’t provide a value for text, validation fails. productId is also not set as Update Model phase was not executed (as expected after validation fails).

Good to know

When a page (view) is requested via a GET and no page params are present, Restore View and Render Respose phases will be executed (same behavior you get in JSF 1.2). Once page params are added to a GET request, JSF will run through the full life cycle in order to convert and validate the values and then set the values into the bean.

More validation

In addition to requiring a value, we can attach other standard validators to page params such as range validator for numbers and length validator for strings. For example:

<f:metadata>
   <f:viewParam name="productId" value="#{bean.productId}" required="true"
	requiredMessage="Product id is required" 
	validatorMessage="Product id must be between 0 and 1000">
	    <f:validateLongRange minimum="0" maximum="1000"/>
   </f:viewParam>
   <f:viewParam name="text" value="#{bean.text}" required="true"
	requiredMessage="Text is required"
	validatorMessage="Text must be at least 3 chars long">
                <f:validateLength minimum="3"/>	
    </f:viewParam>
</f:metadata>

In this example, was added to productId param and to text param. For both custom validation error message was set.

For example the request with the parameters which not meets the requirements specified will produce the following:

Lastly, a custom validator can be attached as well via validator attribute (in which case the method is validation method is placed inside the bean):

<f:viewParam name="text" value="#{bean.text}" validator="#{bean.validateProduct}"/>

or with tag (in which case a class is defined which implements the Validator interface):

<f:viewParam name="text" value="#{bean.text}">
   <f:validator validator="produdIdValidator"/>
</f:viewParam>

Conversation

As with validation, conversation is added in similar fashion. When we do a POST, all values are first converted and then validated. All values entered on a page are sent as strings and need to be converted before pushed into bean properties. Page param values are also sent as plain strings. Taking the productId param, a converter could be added like this:

<f:viewParam name="productId" value="#{bean.productId}" required="true"
	requiredMessage="Product id is required"
	validatorMessage="Product id must be between 0 and 1000"
	converter="javax.faces.Integer"
	converterMessage="Product id must be an integer">
     <f:validateLongRange minimum="0" maximum="1000"/>
</f:viewParam>

This is just to demonstrate how it’s done but we don’t actually have to register the integer converter. Integer converter is one of the default converters in JSF which means that JSF will try to convert the value automatically. The converter attribute could be used to attached a custom converter as well.

Page actions

Page actions can be used to initialize data to be rendered in the view. Page actions are added with new the tag inside view metadata.

<f:metadata>
   <f:viewParam../>
   <f:viewParam.../>
   <f:event type="preRenderView" listener="#{bean.loadAction}" />	
</f:metadata>

Two things are specified above, the event type (preRenderView) and the action listener to be invoked. The preRenderView event type indicates that this listener will be invoked during Render Respone phase.

Just for demonstration purposes, let’s use the productId value as a counter for the number of times to insert text value into a list and then display it.

Bean:

public ArrayList<String> list; // getter
 
public void loadAction() {
    list = new ArrayList<String>();
    for (int i = 0; i < productId ; i++) {
	list.add(text);
    }
}

For this to work, page params need to be set first and that’s exactly what happens. Page params are set into the model during Update Model phase and page action is invoked during Render Response phase (just before the rendering). We use a page param (productId) to decide how many times to insert the text value into the list. To render the list, we use this:

<ol>
   <ui:repeat value="#{bean.list}" var="txt">
      <li>
        #{txt} 
       </li>
    </ui:repeat>
</ol>

Entering the following URL:

http://host:port/app/page.jsf?productId=5&text=JSF2

will produce the following:

Note that if conversation or validation fails, the listener (page action) is still invoked. However, an exception will be thrown as productId is null (unless it’s initialized to some value). One option is to check if any error messages were queued and skip the listener logic. For example:

public void loadAction() {
   List<FacesMessage> messages = FacesContext.getCurrentInstance()
	.getMessageList();
 
   if (messages.size() == 0) {
	list = new ArrayList<String>();
	for (int i = 0; i < productId ; i++) {
		list.add(text);
	}
   }
}

Note: if only page action is present without page params, then only Restore View and Render Response phase are executed.

Summary

Page params and page actions are two great features in JSF 2. Being able to issue a GET with request params also allows to create bookmarkable URLs. Bookmarkable URLs and how page params are tied to navigation as well as the new and tags will be covered in the next article in this series.

Read the original blog entry...

More Stories By Max Katz

Max Katz heads Developer Relations for Appery.io, a cloud-based mobile app platform. He loves trying out new and cool REST APIs in mobile apps. Max is the author of two books “Practical RichFaces” (Apress 2008, 2011), DZone MVB (Most Valuable Blogger), and is a frequent speaker at developer conferences. You can find out what Max is up to on his blog: http://maxkatz.org and Twitter: @maxkatz.