Getting around JSF: The role of JSP

Learn how to use JavaServer Pages with JavaServer Faces

1 2 Page 2
Page 2 of 2

In this example, both JSTL and JSF tags are nested within the JSF <f:view> tag, which defines the start of the JSF component tree. The example uses the JSF HtmlOutputText component (<h:outputText>) and the JSTL <c:out> tag to display text. A JSTL <c:import> tag includes the system's web.xml file in the page (this isn't exactly something you want to share with others, so don't do this on a real server). Because web.xml is an XML file, the <c:import> tag is nested in an <f:verbatim> tag, which is a JSF UIOutput component whose renderer escapes the XML so it can be displayed normally in an HTML page. This example doesn't do much, but it does demonstrate the ability to use different tags on the same page together.

Note that we nested a JSTL tag inside the JSF <f:verbatim> tag. In general, it's easier to nest JSF tags inside other tags than vice versa. As a matter of fact, any component that displays its own children, like HtmlDataTable and HtmlPanelGrid, requires that any template text or nested tags be within an <f:verbatim> tag.

What's great about using JSTL tags with JSF tags is that they both use similar expression languages to reference objects (this is true for JSP 2.0's expression language as well). This allows you to easily share data between JSTL and JSF tags in an intuitive manner. To illustrate this point, let's look at another example that allows the user to input a number into an HtmlInputText control and then uses that value to display a string repeatedly with a JSTL <c:forEach> tag. This code is shown in Listing 2.

Listing 2. Using JSF and JSTL tags together with the same backing bean

 ...
<f:view>
   <jsp:useBean class="org.jia.examples.TestForm" id="exampleBean" scope="session"/>
   <h1>
      <h:outputText value="Example of using JSF and JSTL expression languages"/>
   </h1>
   <h:form>
      <h:outputLabel for="inputInt">
         <h:outputText value="How many times do you want to repeat the Oracle's prophecy?"/>
      </h:outputLabel>
      <h:inputText id="inputInt" value="#{sessionScope.exampleBean.number}"/>
      <h:commandButton value="Go!"/>
      <p>
         <c:if test="${sessionScope.exampleBean.number > 0}">
            <c:forEach begin="0" end="${sessionScope.exampleBean.number - 1}" var="count">
               Queen Tracey will achieve world domination.<br>
            </c:forEach>
         </c:if>
      </p>
   </h:form>
...
</f:view>
...

Warning: If you're using JSP or JSTL expressions with managed beans, you need to ensure that the beans have been created first, either by a JSF expression, Java code, or your own custom tag. This is because these older expression languages don't know about JSF's Managed Bean Creation facility.

This listing references a JavaBean, called exampleBean that has a number property of type int. An HtmlInputText component is used to update the value of the bean's property based on user input. When the user clicks the Go! button (an HtmlCommandButton component), the number property is updated and the page is redisplayed. When this happens, the JSTL <c:forEach> tag repeats the text displayed by a JSTL <c:out> tag exampleBean.number times. The <c:forEach> tag only executes if exampleBean.number is greater than 0; this is controlled by a JSTL <c:if> tag.

You cannot use JSF component tags inside tags that iterate over their body, like the JSTL <c:forEach> tag. The recommended workaround is to use the HtmlDataTable component or another component iterates over a dataset or collection.

In this example, there are no JSF components nested inside the JSTL <c:if> tag. But what happens if a component is displayed once and then hidden by a conditional tag like <c:if> when the page is redisplayed? The first time the component is displayed, it will be added to the view. The second time, if the <c:if> tag doesn't display the component, JSF will delete it from the view. This means that any input controls will lose their local values, and that you won't be able to reference these components (via client identifiers or in code). As an example, take a look at Listing 3, which is from the same page as Listing 2.

Listing 3. Conditionally displaying JSF components with JSTL tags

 ...
<h:form>
   <h:outputText value="If you entered a number greater than 10,
      two input controls will display below."/>
   <p>
      <c:if test="${sessionScope.exampleBean.number > 10}">
         <h:outputLabel id="inputStringLabel"for="inputString">
            <h:outputText id="outputStringLabel" value="Enter in your string. 
               JSF will remember the value unless this control is hidden."/>
            </h:outputLabel>
            <h:inputText id="inputString"/>
            <h:commandButton value="Go!"/>
      </c:if>
   </p>
</h:form>
...

The JSTL <c:if> tag will execute its body if the value of exampleBean.number is greater than 10. If the body is executed, then all of the nested components will be added to the view and displayed. If not, the components will be removed (if they have been added previously). This is shown graphically in Figure 1. If you control visibility of components with JSTL conditional tags (or other custom tags), the components will be removed from the view if they're not displayed. This means that the components will forget their local values as well.

Figure 1. The JSTL <c:if> tag will execute its body if the value of exampleBean.number is greater than 10. Click on thumbnail to view full-sized image.

Figure 2 shows the output of the JSP page used for Listings 2 and 3. The value of the input field at the top (an HtmlInputText component) is wired to the exampleBean.number backing bean property, which is used by the JSTL <c:forEach> tag to display a string exampleBean.number times. In the bottom portion of the page, a JSTL <c:if> tag shows a form with JSF components if exampleBean.number is greater than 10. Otherwise, the components will not be displayed, and they are removed from the view (and the input control will lose its value).

Figure 2. The output of the JSP page shown in Listings 2 and 3. Click on thumbnail to view full-sized image.

You can achieve the same effect as the code in Listing 3 by placing these components in an HtmlPanelGroup and setting its rendered property to equal the same expression. An HtmlPanelGroup is used as a container for multiple components. Here's an example:

 <h:panelGroup rendered="#{sessionScope.exampleBean.number > 10}">
   <h:outputLabel id="inputStringLabel2"for="inputString">
      <h:outputText id="outputStringLabel2" value="Enter in your string. JSF
         will remember the value."/>
      </h:outputLabel>
   <h:inputText id="inputString2"/>
   <h:commandButton value="Go!"/>
</h:panelGroup>

If exampleBean.number is greater than 10, this panel becomes visible. In this case, the components won't be deleted if they're not displayed. This is a good example of the types of things you can do with pure JSF tags without JSTL.

Tip: Even though custom tags like the ones provided by the JSTL provide a lot of functionality, if you're developing from scratch (or refactoring), you should first look to see if you can implement the desired behavior with standard JSF components. Using good components and well-designed backing beans, you can usually avoid the need for many JSTL tags in your pages. You can hide or display entire panels and do all sorts of powerful things with standard JSF.

Here are a few other interoperability constraints for using JSF tags with JSTL internationalization and formatting tags:

  • Use of <fmt:parseDate> and <fmt:parseNumber> is not recommended. You should use the HtmlInputText component with a DateTime or Number converter.
  • The <fmt:requestEncoding> tag, which is used to determine or specify the character encoding for the page, should not be used. Usually, JSF handles this automatically, and if you need to force a particular encoding, you should use the JSP page directive: <%page contentType="[contenttype];[charset]"%>.
  • The <fmt:setLocale> tag shouldn't be used either. Because it doesn't know about JSF, it may cause your JSTL tags to use one locale and your JSF components may use another, which is a recipe for disaster. Instead, you should use JSF's internationalization features. To control the locale for a particular page, use the locale property of the UIViewRoot component. JSF's internationalization features work for both JSF and JSTL.

Combining JSF with the JSTL can be quite powerful. Custom tags that you have developed or obtained from third parties should work with JSF as well as the JSTL tags we've shown here. In general, though, you should stick with JSF tags when possible.

Kito D. Mann is a consultant specializing in enterprise architecture, mentoring, and development. A programmer since the tender age of 12, he has written several articles on Java-related technologies, and also speaks at user groups and conferences. He has consulted with several Fortune 500 companies and has been the chief architect of an educational application service provider. Mann is also the founder of the JSF Central community Website, and a member of the JSF 1.2 and JSP 2.1 expert groups. He holds a B.A. in computer science from Johns Hopkins University and lives in Stamford, Connecticut, with his wife, four cats, and two parrots. In his spare time, he enjoys making incomplete compositions with electronic music equipment.

Learn more about this topic

1 2 Page 2
Page 2 of 2