Home  > Resources  > Blog

Understanding JSF View Parameters

 
May 17, 2012 by Bibhas Bhattacharya

JSF 2.0 adds support for view parameters. This, essentially adds some support for GET request making it possible to bookmark some of the pages. This feature was sorely lacking in JSF 1.x and is much welcomed by the development community. However, I was surprised by the lack of any good tutorial showing any real life use of the feature.

Primary use of a bookmarkable dynamic page is to lookup some data. The identifier of the data is provided in the URL parameters. For example:

  • /TrackOrderStatus?orderId=1029
  • /FindCustomer?id=1928

This wasn’t possible in JSF 1.x. For developers who already know JSF 1.x, view parameters are best learned by migrating an existing application.

Let’s say that we have a simple customer lookup application. We will first build it the old way that uses POST request.

The Customer class is as follows.

public class Customer {
	String id;
	String name;
	
	public Customer(String id, String name) {
		this.id = id;
		this.name = name;
	}
	
	public Customer() {
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

The CustomerDAO class looks like this with a dummy in-memory database.

public class CustomerDAO {
	static HashMap<String, Customer> data;
	
	static {
		data = new HashMap<String, Customer>();
		
		data.put("E001", new Customer("E001", "Bugs Bunny"));
		data.put("E002", new Customer("E002", "Daffy Duck"));
		data.put("E003", new Customer("E003", "Samity Sam"));
	}
	
	public Customer findCustomer(String id) {
		return data.get(id);
	}
}

Finally, the JSF managed bean looks like this.

@ManagedBean
@RequestScoped
public class MainController {
	String lookupId;
	Customer customer = new Customer();
	
	public String getLookupId() {
		return lookupId;
	}
	public void setLookupId(String lookupId) {
		this.lookupId = lookupId;
	}
	public Customer getCustomer() {
		return customer;
	}
	public void setCustomer(Customer customer) {
		this.customer = customer;
	}
	public String doLookup() {
		CustomerDAO dao = new CustomerDAO();
		
		customer = dao.findCustomer(lookupId);
		
		return "result";
	}
}

Now, we can get to the page design. The index.xhtml page has a form where user can type in a customer ID and perform a lookup.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns_ui="http://java.sun.com/jsf/facelets"
	xmlns_f="http://java.sun.com/jsf/core"
	xmlns_h="http://java.sun.com/jsf/html">
<h:head>
	<title>Home</title>
</h:head>
<h:body>
	<h:form>
	Search customer by id:<br />
		<h:inputText value="#{mainController.lookupId}"/><br />
		<h:commandButton type="submit" value="Submit"
			action="#{mainController.doLookup}" />
	</h:form>
</h:body>
</html>

The result.xhtml page looks like this. Note: JSF 1.x developers pay attention. The doLookup() method returns an outcome of "result". In absence of any matching navigation rule, JSF 2.0 will navigate to result.xhtml.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns_ui="http://java.sun.com/jsf/facelets"
	xmlns_f="http://java.sun.com/jsf/core"
	xmlns_h="http://java.sun.com/jsf/html">
<h:head>
	<title>Result</title>
</h:head>
<h:body>
	<p>
		<b>Name:</b> ${mainController.customer.name}
	</p>
</h:body>
</html>

This is pretty straightforward and works almost the same way as things did in JSF 1.0 (except there is no need for faces-config.xml for a simple app like this). After you deploy the application, you can execute index.xhtml.

image

Enter a valid id and click Submit. You should see the result.

image

So far so good. Except, we are using POST request and the result page can’t be book marked. How can we migrate that to use GET? The answer is view parameters. This will need a change in the result.xhtml page. We need to do two things:

  1. Map URL parameters in a GET request to managed bean properties.
  2. Have the action method doLookup() invoked so that we can populate the Customer bean.

Open result.xhtml in editor. Between the head and body tags, define the view parameter:

<h:head>
	<title>Result</title>
</h:head>
<f:metadata>
	<f:viewParam name="id" value="#{mainController.lookupId}" />
	<f:event listener="#{mainController.doLookup}" 
		type="preRenderView"/>
</f:metadata>
<h:body>
	<p>
		<b>Name:</b> ${mainController.customer.name}
	</p>
</h:body>

There are many things going on here:

  1. <f:viewParam> must appear within <f:metadata>.
  2. <f:metadata> must appear directly under the view root. That is, you can not define it within <h:head> or <h:body>.
  3. The name attribute of <f:viewParam> defines the name of the URL parameter. This value is set to the property identified by the value attribute. That means, in our case, the id URL parameter will be set as the lookupId property of MainController.
  4. The <f:event> tag is used to have the action handler method invoked when a GET request arrives for result.xml.

After you deploy the application, you should be able to execute the result page directly by supplying the id URL parameter. For example: http://localhost:9080/JSFTest/result.faces?id=E001. Depending on the URI path binding of the JSF servlet, it can also be: http://localhost:9080/JSFTest/faces/result.xhtml?id=E001.

You can put hyperlinks in your application using <h:link outcome="result?id=E001">Bugs</h:link>.

There is a slight problem at this point. If you submit the original form, doLookup() will be called twice – first time due the POST request and next time due to the <f:event> tag in result.xhtml. The solution is to change the action of the commandButton to an outcome, rather than an action handler method. Open index.xhtml and change the action of the commandButton to "result" outcome.

<h:form styleClass="form" id="form1">
Search customer by id:<br />
	<h:inputText value="#{mainController.lookupId}"></h:inputText>
	<br />
	<h:commandButton type="submit" value="Submit"
		action="result" />
</h:form>

Follow Us

Blog Categories