Facelets seems to really scratch an itch for the JSF development community. I've gotten a lot of positive feedback from my first article about Facelets, and I'm looking forward to showing you more fun stuff you can do with Facelets in this article. Kudos to Jacob Hookom and everyone else involved in the development of Facelets!
In my last article, I introduced the concept behind Facelets and showed you how to create and manipulate its HTML-style templates and reusable composition components. In this article, I build on that discussion, using many of the same examples and components I introduced then. For starters, I show you a pain-free way to do Internationalization using a Facelets expression language (EL) function. Next, I show you how to create reasonable defaults and custom logic tags. Finally, I show you how to do lightweight metaprogramming in Facelets.
Because so many of the examples in this article build on the ones from my previous article, I strongly suggest you read that article first. You might also want to download the example code for this article and install Facelets (and Tomahawk) before going further.
Internationalization made easy
Internationalization doesn't have to hurt when you're working with Facelets. For the first exercise, I show you how to extend the field composition component from the last article to work with dates, Booleans, and all manner of Java types that should be displayed as text. I also show you how to use reasonable defaults to internationalize the label for the field.
Recall from last time that the field composition component uses the field name as its default label if the label is not passed, as shown here:
...
<!-- The label is optional. Use fieldName as label if label missing. -->
<c:if test="${empty label}">
<c:set var="label" value="${fieldName}" />
</c:if>
|
Say you would like to change that. Instead of loading the
fieldName as its default label, you want the field
composition component to look up the fieldName in the
resource bundle associated with Faces. For this, you create a tag
library that defines tags and EL functions. First,
you create an EL function that looks up a fieldName in
the resource bundle. If it can't find the label in the resource bundle,
it tries to generate a fieldName based on the camel case of the given fieldName. Listing 1 shows an example usage:
Listing 1. Example usage of the EL function
<html xmlns="http://www.w3.org/1999/xhtml"
...
xmlns:arc="http://www.arc-mind.com/jsf/core"
xmlns:t="http://myfaces.apache.org/tomahawk">
<!-- The label is optional. Generate it if it is missing. -->
<c:if test="${empty label}">
<c:set var="label"
value="${arc:getFieldLabel(fieldName,namespace)}" />
</c:if>
|
In Listing 1, I the EL function arc:getFieldLabel(fieldName,namespace) to look up
the fieldName in the resource bundle
associated with Faces. The getFieldLabel()
method actually looks up the label in the resource bundle. Here are the
steps to create an EL function:
- Create a
TagLibraryclass. - Create a Java utility class.
- Register the
getFieldLabel()method. - Register the
TagLibraryclass. - Register the facelets-taglib descriptor file.
The following sections explain these steps one by one and show you how to use your new EL function.
Step 1. Create a TagLibrary class
EL functions and logic tags are defined in a Facelets TagLibrary class. To create a TagLibrary, class you need to subclass
AbstractTagLibrary, as shown in Listing 2:
Listing 2. Subclassing AbstractTagLibrary
package com.arcmind.jsfquickstart.tags;
import com.sun.facelets.tag.AbstractTagLibrary;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* JsfCoreLibrary is an example for IBM developerWorks (c).
* @author Rick Hightower from ArcMind Inc. http://www.arc-mind.com
*/
public final class JsfCoreLibrary extends AbstractTagLibrary {
/** Namespace used to import this library in Facelets pages */
public static final String NAMESPACE = "http://www.arc-mind.com/jsf/core";
/** Current instance of library. */
public static final JsfCoreLibrary INSTANCE = new JsfCoreLibrary();
...
...
|
Once the tag library is defined, you can add EL functions and logic tags to it.
Step 2. Create a Java utility class
Next you need to create a Java utility class that has a static method
called getFieldLabel(). This utility method has
no special tie to Facelets; it's just a regular Java static method, as
shown in Listing 3:
Listing 3. The Java utility class with getFieldLabel()
package com.arcmind.jsfquickstart.tags;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.faces.context.FacesContext;
/**
* Functions to aid developing JSF applications.
*/
public final class JsfFunctions {
/**
* Stops creation of a new JsfFunctions object.
*/
private JsfFunctions() {
}
/**
* Get the field label.
*
* @param fieldName
* fieldName
* @param formId
* form id
* @return Message from the Message Source.
*/
public static String getFieldLabel(final String fieldName,
final String formId) {
Locale locale = FacesContext.getCurrentInstance().getViewRoot()
.getLocale();
String bundleName = FacesContext.getCurrentInstance().getApplication()
.getMessageBundle();
ResourceBundle bundle = ResourceBundle
.getBundle(bundleName, locale, getClassLoader());
/** Look for formId.fieldName, e.g., EmployeeForm.firstName. */
String label = null;
try {
label = bundle.getString(formId + fieldName);
return label;
} catch (MissingResourceException e) {
// do nothing on purpose.
}
try {
/** Look for just fieldName, e.g., firstName. */
label = bundle.getString(fieldName);
} catch (MissingResourceException e) {
/**
* Convert fieldName, e.g., firstName automatically becomes First
* Name.
*/
label = generateLabelValue(fieldName);
}
return label;
}
private static ClassLoader getClassLoader() {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
if (classLoader == null) {
return JsfFunctions.class.getClassLoader();
}
return classLoader;
}
/**
* Generate the field. Transforms firstName into First Name. This allows
* reasonable defaults for labels.
*
* @param fieldName
* fieldName
*
* @return generated label name.
*/
public static String generateLabelValue(final String fieldName) {
StringBuffer buffer = new StringBuffer(fieldName.length() * 2);
char[] chars = fieldName.toCharArray();
/* Change firstName to First Name. */
for (int index = 0; index < chars.length; index++) {
char cchar = chars[index];
/* Make the first character uppercase. */
if (index == 0) {
cchar = Character.toUpperCase(cchar);
buffer.append(cchar);
continue;
}
/* Look for an uppercase character, if found add a space. */
if (Character.isUpperCase(cchar)) {
buffer.append(' ');
buffer.append(cchar);
continue;
}
buffer.append(cchar);
}
return buffer.toString();
}
}
|
The getFieldLabel() method shown in Listing
3 looks up the field in the resource bundle associated with JSF. If it
cannot find the field, it splits and capitalizes the camel-case string,
such that a "firstName" field name becomes "First Name." This gives you
a reasonable default, which you can still set up in the resource bundle
if you want to override it.
Step 3. Register the getFieldLabel method
In the AbstractTagLibrary class shown in Listing 2 (JsfCoreLibrary), you need to register the getFieldLabel() method with the base class so that it
is available as an EL function. I do this in Listing 4:
Listing 4. Registering JsfCoreLibrary.java as an EL function
public JsfCoreLibrary() {
super(NAMESPACE);
try {
Method[] methods = JsfFunctions.class.getMethods();
for (int i = 0; i < methods.length; i++) {
if (Modifier.isStatic(methods[i].getModifiers())) {
this.addFunction(methods[i].getName(), methods[i]);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
|
As you can see, Listing 4 just iterates through all the methods
on the JsfFunction class using reflection. It
then adds all JsfFunction's static methods
using the inherited method addFunction().
Step 4. Register the TagLibrary class
You've now defined the EL function and the TagLibrary class that contains it. Next, you need
to register your new TagLibrary class so that
it can be found by Facelets. You do this by declaring a new library
class in a new facelets-taglib file called jsf-core.taglib.xml, as shown
in Listing 5:
Listing 5. jsf-core.taglib.xml
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"facelet-taglib_1_0.dtd">
<facelet-taglib>
<library-class>
com.arcmind.jsfquickstart.tags.JsfCoreLibrary
</library-class>
</facelet-taglib>
|
Step 5. Register the facelets-taglib descriptor file
For Facelets to find your tag library, you need to register it in the WEB-INF/web.xml file, as shown in Listing 6:
Listing 6. Registering the new taglib with Facelets
<context-param> <param-name>facelets.LIBRARIES</param-name> <param-value> /WEB-INF/facelets/tags/arcmind.taglib.xml; /WEB-INF/facelets/tags/jsf-core.taglib.xml </param-value> </context-param> |
Notice that I added jsf-core.taglib.xml to the semicolon-delimited list
passed to the init-param facelets.LIBRARIES. Composition components
go in their own taglib file. EL functions and Facelets logic tags can
go in the same taglib file.
Using the EL function in a composition component
Now that you've defined the EL function, placed it in the Facelets taglib
descriptor file, and registered the taglib descriptor file in web.xml, you can
start to use the tag. In the field composition component, you can use the
arc:getFieldLabel to look up the label in the resource bundle, as shown in Listing 7:
Listing 7. Using the EL function
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:arc="http://www.arc-mind.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:t="http://myfaces.apache.org/tomahawk">
THIS TEXT WILL BE REMOVED
<ui:composition>
<c:if test="${empty label}">
<c:set var="label"
value="${arc:getFieldLabel(fieldName,namespace)}" />
</c:if>
...
|
Notice I imported that "arc" library by specifying its namespace in
the xmlns:arc="http://www.arc-mind.com/jsf/core" HTML element. The
namespace has to match the namespace declared in the TagLibrary class (JsfCoreLibrary in this case) as shown here:
public final class JsfCoreLibrary extends AbstractTagLibrary {
/** Namespace used to import this library in Facelets pages */
public static final String NAMESPACE = "http://www.arc-mind.com/jsf/core";
|
This might seem like a lot of work, even for just five steps. However, once you've done this basic setup, you can easily add more logic tags and EL functions without going through the same hassle every time. You'll have reasonable defaults for all your field label names. And, if your application only supports your native tongue and location, then for most cases, you won't have to add entries in the resource bundle. You'll only have to add entries in the resource bundle if you want a special label or if you start to use your application for other languages and/or locations.
Facelets solves a pet peeve of mine about internationalization: writing a lot of special code for something I may never need. Now it's free! Next, let's see what happens if you decide you want to change the field tag so that it generates Booleans, dates, and text components automatically.
First, you want to add some metaprogramming capabilities to the field.jsp tag. For this, you create Facelets
logic tags that tell you if a particular value binding type is a
Boolean (isBoolean), some form of text (isText), or a date (isDate). Then you use the tags to render the
appropriate component.
Listing 8 shows the usage for these tags (field.xhtml):
Listing 8. field.xhtml
<!-- Initialize the value binding -->
<arc:setValueBinding var="vb" valueBinding="#{entity[fieldName]}"/>
<!-- If the value binding is a string, display an inputText field. -->
<arc:isText id="vb">
<h:inputText id="#{fieldId}" value="#{entity[fieldName]}"
required="${required}" styleClass="fieldInput">
<ui:insert />
</h:inputText>
</arc:isText>
<!-- If the value binding is a boolean, display a
selectBooleanCheckbox field. -->
<arc:isBoolean id="vb">
<h:selectBooleanCheckbox id="#{fieldId}"
value="#{entity[fieldName]}" required="${required}"/>
</arc:isBoolean>
<!-- If the value binding is a date, display a t:inputDate field. -->
<arc:isDate id="vb">
<t:calendar id="#{fieldId}" renderPopupButtonAsImage="true"
renderAsPopup="true"
value="#{entity[fieldName]}"
required="${required}"
styleClass="fieldInput" >
<ui:insert />
</t:calendar>
</arc:isDate>
...
|
The field.xhtml code in Listing 8 renders an inputText if the value binding is some sort of
text. It renders a selectBooleanCheckbox if
the value binding type is a Boolean. Finally, it renders a Tomahawk
calendar component if it is a date (see "About
Tomahawk").
Next, you create a value binding to get the type that is being bound to a particular component. The value binding has information
about the type of value the component is bound to. If you've used JSF,
then you're already familiar with value bindings; for example, #{Employee.firstName} is a value binding tag that
likely equates to a string type while the #{Employee.age} tag likely equates to an integer
type. In the next section, you learn how to create a value binding tag
using Facelets.
There are four steps to creating a tag that retrieves a value binding in Facelets:
- Create a class that subclasses the
TagHandler. - Register the attributes in the constructor.
- Override the apply method.
- Register the tag in the
JsfCoreLibraryclass.
The following sections explain each of the steps to build a value-binding tag and then show you how to use it.
Step 1. Create a class that subclasses TagHandler
You can use a Facelets TagHandler to
inject the logic to decide whether components in the body of a tag are
added to the component tree. Remember that the endgame of a Facelets
template is to create a JSF component tree.
Listing 9 shows how the SetValueBindingHandler tag subclasses TagHandler. The SetValueBindingHandler just defines a variable and
puts the variable in the Facelets scope so your logic tags can reuse the
variable.
Listing 9. SetValueBindingHandler subclasses TagHandler
package com.arcmind.jsfquickstart.tags;
import com.sun.facelets.FaceletContext;
import com.sun.facelets.tag.TagAttribute;
import com.sun.facelets.tag.TagConfig;
import com.sun.facelets.tag.TagHandler;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
/**
* Maps value binding and type so other tags can access it.
*/
public final class SetValueBindingHandler extends TagHandler {
|
Step 2. Register the attributes in the constructor
When you write Facelets tags, it is important to realize that the
attributes are defined by the tag itself and not in an XML file.
The constructor of SetValueBindingHandler defines and registers two attributes (var and valueBinding) in the constructor, as shown in Listing 10:
Listing 10. Register the attributes in the constructor
/**
* Maps value binding and type so other tags can access it.
*/
public final class SetValueBindingHandler extends TagHandler {
/** The name of the new variable that this tag defines. */
private final TagAttribute var;
/** The actual value binding expression. */
private final TagAttribute valueBinding;
/**
* Constructor. Set up the attributes for this tag.
*
* @param config TagConfig
*/
public SetValueBindingHandler(final TagConfig config) {
super(config);
/* Define var and valueBinding attributes. */
this.var = this.getRequiredAttribute("var");
this.valueBinding = this.getRequiredAttribute("valueBinding");
}
|
Step 3. Override the apply method
Next, you override the apply method in SetValueBindingHandler. Typically, you override the
apply method to programmatically decide if the components defined in the
body of the tag are added to the component tree. However, SetValueBindingHandler just defines a variable
(whatever var is set to; for example, "vb") to the value binding that
was passed. Listing 11 shows the code to override the apply method:
Listing 11. Override the apply method
/**
* Maps value binding and type so other tags can access it.
*/
public final class SetValueBindingHandler extends TagHandler {
...
...
/**
* Apply.
*
* @param faceletsContext faceletsContext
* @param parent parent
*
* @throws IOException IOException
*/
public void apply(final FaceletContext faceletsContext, final UIComponent parent) {
/* Create the ValueExpression from the valueBinding attribute. */
ValueExpression valueExpression =
this.valueBinding.getValueExpression(faceletsContext, Object.class);
/* Get the name of the new value. */
String tvar = this.var.getValue(faceletsContext);
Class type = valueExpression.getType(faceletsContext);
/* Put the value binding into the FaceletsContext where
* we can retrieve it from other components.
*/
faceletsContext.setAttribute(tvar, valueExpression);
/* Cache the type so we don't have to look it
* up in each tag. */
faceletsContext.setAttribute(tvar + "Type", type);
}
|
Listing 11 defines two variables: var and var + "Type". Next, you simply put the variables into the faceletsContext (see my comments in Listing 11 for more details).
Step 4. Register the tag in the JsfCoreLibrary class
You can use the same JsfCoreLibrary for
this example as for the previous one. A TagLibrary can contain both EL functions and logic
tags. So, for the final step of this exercise, you register the
SetValueBindingHandler tag in the JsfCoreLibrary by calling the superclass addTagHandler, as shown in Listing 12:
Listing 12. Register the tag in the JsfCoreLibrary
/**
* JsfCoreLibrary is an example for IBM developerWorks (c).
* @author Rick Hightower from ArcMind Inc. http://www.arc-mind.com
*/
public final class JsfCoreLibrary extends AbstractTagLibrary {
/** Namespace used to import this library in Facelets pages */
public static final String NAMESPACE = "http://www.arc-mind.com/jsf/core";
/** Current instance of library. */
public static final JsfCoreLibrary INSTANCE = new JsfCoreLibrary();
/**
* Creates a new JsfCoreLibrary object.
*
*/
public JsfCoreLibrary() {
super(NAMESPACE);
this.addTagHandler("setValueBinding", SetValueBindingHandler.class);
...
}
|
The nice thing about Facelets is that if you compare developing tags in
Facelets to writing equivalent tags in JSP, it takes a lot less XML. You can
easily develop many more tags and register them in the JsfCoreLibray.
Notice that the name of the tag is setValueBinding.
Now that you've defined the tag, setValueBinding, you can start using it, as shown
in Listing 13:
Listing 13. Using the setValueBinding tag
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:arc="http://www.arc-mind.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:t="http://myfaces.apache.org/tomahawk">
THIS TEXT WILL BE REMOVED
<ui:composition>
...
<!-- Initialize the value binding -->
<arc:setValueBinding var="vb"
valueBinding="#{entity[fieldName]}" />
|
You don't have to import anything or set up anything in web.xml. Once you define a tag library (like "arc"), you can add tags and functions to it without updating any XML files. Yes, you can escape XMHell!
For a last little bit of Facelets fun (for this article), you'll create a tag that allows you to add components defined in the body if the value binding is of a certain type.
The magical tag, IsTypeHandler(),
selectively decides if a component gets added to the component tree. The
tags calls this.nextHandler.apply(faceletsContext,
aParent) if the components defined in the body should be added to
the component tree. The isTypeHandler() calls
the abstract method isType() to determine if it
should call this.nextHandler.apply(faceletsContext,
aParent) and add the components defined in the body of tag
handler to the component tree. Listing 14 shows the base class for all of this:
Listing 14. Base class for handling types
package com.arcmind.jsfquickstart.tags;
import com.sun.facelets.FaceletContext;
import com.sun.facelets.tag.TagAttribute;
import com.sun.facelets.tag.TagConfig;
import com.sun.facelets.tag.TagHandler;
import java.io.IOException;
import javax.faces.component.UIComponent;
/**
* Is the current field a boolean.
*/
public abstract class IsTypeHandler extends TagHandler {
/** */
private final TagAttribute id;
/**
* Create tag.
*
* @param config TagConfig
*/
public IsTypeHandler(final TagConfig config) {
super(config);
this.id = this.getRequiredAttribute("id");
}
/**
* Is the current field a boolean.
*
* @param faceletsContext ctx
* @param aParent parent
*
* @throws IOException IOException
*/
public void apply(final FaceletContext faceletsContext, final UIComponent aParent)
throws IOException {
/* Get the name of the value binding. */
String tid = this.id.getValue(faceletsContext);
Class type =
(Class) faceletsContext.getVariableMapper().resolveVariable(tid + "Type")
.getValue(faceletsContext);
/* If the type is a boolean, process the body of the tag.
*/
if (isType(type)) {
this.nextHandler.apply(faceletsContext, aParent);
}
}
/**
*
*
* @param type type
*
* @return true if this is the correct type.
*/
protected abstract boolean isType(Class type);
}
|
Next, you add a tag per type that you want to handle, as I do in Listing 15:
Listing 15. Handling specific types
package com.arcmind.jsfquickstart.tags;
import com.sun.facelets.tag.TagConfig;
/**
* Is the current field a boolean.
*/
public final class IsBooleanHandler extends IsTypeHandler {
/**
* Create tag.
*
* @param config TagConfig
*/
public IsBooleanHandler(final TagConfig config) {
super(config);
}
/**
*
*
* @param type type
*
* @return true if this is a boolean.
*/
protected boolean isType(final Class type) {
/* If the type is a boolean, process the body of the tag.
*/
if (type == Boolean.class) {
return true;
} else if (type.isPrimitive() && "boolean".equals(type.getName())) {
return true;
}
return false;
}
}
package com.arcmind.jsfquickstart.tags;
import java.util.Date;
import com.sun.facelets.tag.TagConfig;
/**
* Is the current field a date.
*/
public final class IsDateHandler extends IsTypeHandler {
/**
* Create tag.
*
* @param config TagConfig
*/
public IsDateHandler(final TagConfig config) {
super(config);
}
/**
*
*
* @param type type
*
* @return true if this is a boolean.
*/
protected boolean isType(final Class type) {
/* If the type is a string, process the body of the tag.
*/
if (type == Date.class) {
return true;
}
return false;
}
}
package com.arcmind.jsfquickstart.tags;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.sun.facelets.tag.TagConfig;
/**
* Is the current field a string.
*/
public final class IsTextHandler extends IsTypeHandler {
/**
* Create tag.
*
* @param config TagConfig
*/
public IsTextHandler(final TagConfig config) {
super(config);
}
/**
*
*
* @param type type
*
* @return true if this is a boolean.
*/
protected boolean isType(final Class type) {
/* If the type is a string, process the body of the tag.
*/
if (type == String.class
|| type == Integer.class
|| type == BigDecimal.class
|| type == BigInteger.class
|| type == Character.class
|| type == Long.class
|| type == Short.class
|| type == Byte.class
|| type == Float.class
|| type == Double.class
|| (type.isPrimitive() && !type.getName().equals("boolean"))
) {
return true;
} else {
return false;
}
}
}
|
Listing 15 just uses the type of the value binding to decide if
the components defined in the tag's bodies get added to the component tree.
Each tag overrides the isType() method.
Currently, I'm using just the type to determine if the body components
get added to the tree, but you could easily use annotations or other
forms of metadata if you wanted.
Naturally, your task isn't complete until you register all of
the above tags with the JsfCoreLibrary, as shown in Listing 16:
Listing 16. Adding the type handling tags to the TagLibrary
public final class JsfCoreLibrary extends AbstractTagLibrary {
...
public JsfCoreLibrary() {
...
this.addTagHandler("isBoolean", IsBooleanHandler.class);
this.addTagHandler("isText", IsTextHandler.class);
this.addTagHandler("isDate", IsDateHandler.class);
...
|
Now you have three more tags in your taglibrary: isBoolean, isText, and isDate().
You can use these tags in your field tag as shown in Listing 17:
Listing 17. Final field.xhtml
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:arc="http://www.arc-mind.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:t="http://myfaces.apache.org/tomahawk">
THIS TEXT WILL BE REMOVED
<ui:composition>
<!-- The label is optional. Generate it if it is missing. -->
<c:if test="${empty label}">
<c:set var="label"
value="${arc:getFieldLabel(fieldName,namespace)}" />
</c:if>
<!-- The required attribute is optional,
initialize it to true if not found. -->
<c:if test="${empty required}">
<c:set var="required" value="true" />
</c:if>
<h:panelGroup>
<h:outputLabel id="${fieldName}Label"
value="${label}" for="#{fieldName}" />
</h:panelGroup>
<!-- Initialize the value binding -->
<arc:setValueBinding var="vb"
valueBinding="#{entity[fieldName]}" />
<!-- If the value binding is a string,
display an inputText field. -->
<arc:isText id="vb">
<h:inputText id="#{fieldName}"
value="#{entity[fieldName]}"
required="${required}" styleClass="fieldInput">
<ui:insert />
</h:inputText>
</arc:isText>
<!-- If the value binding is a boolean, display a
selectBooleanCheckbox field. -->
<arc:isBoolean id="vb">
<h:selectBooleanCheckbox id="#{fieldName}"
value="#{entity[fieldName]}"
required="${required}" />
</arc:isBoolean>
<!-- If the value binding is a date,
display a t:inputDate field. -->
<arc:isDate id="vb">
<t:calendar id="#{fieldName}" renderPopupButtonAsImage="true"
renderAsPopup="true" value="#{entity[fieldName]}"
required="${required}" styleClass="fieldInput">
<ui:insert />
</t:calendar>
</arc:isDate>
<!-- Display any error message that are found -->
<h:message id="${fieldName}Message"
style="color: red; text-decoration: overline"
for="#{fieldName}" />
</ui:composition>
THIS TEXT WILL BE REMOVED AS WELL
</html>
|
Your field tag now displays an inputText
field if the value binding is of type Integer, String, BigDecimal,
Character, Long, Short, Byte, Float, Double, short, int, char, etc. The
field tag displays a check box if the value binding type is a boolean or
a Boolean. And it displays a Tomahawk calendar component if the value
binding is of type java.util.Date -- which, frankly, is very cool.
In this article, I've shown you some fun and fairly powerful applications of Facelets. The Facelets EL function takes a lot of the pain out of internationalization, and custom logic tags allow you to extend Facelets' built-in logic tags. Along the way, you've seen how easy it was to extend the simple examples from my previous article with more advanced functionality. I showed you how to update the field composition component to work with dates (Tomahawk calendar), Booleans (check boxes), and all manner of Java types that should be displayed as text. I also introduced you to reasonable defaults, which I used to internationalize the label for the field component. Keep an eye out for more fun with Facelets in the coming months!
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code | j-facelets2.zip | 12KB | HTTP |
Information about download methods
Learn
- "Facelets fits JSF like a glove" (Richard Hightower, developerWorks, February 2006): Get started with the first real solution to your JSF-and-JSP woes.
- Kiss my app (Jacob Hookom's blog, May 2005): Candid thoughts about Facelets from its creator.
- "Inside Facelets, Part 1: An Introduction" (Jacob Hookom, JSF Central, August 2005): A more
formal introduction to Facelets.
- Use Facelets with Tomahawk (MyFaces WiKi, Apache.org): An ongoing discussion about using
Facelets with the MyFaces Tomahawk extension.
- "Design with the JSF architecture" (Anand Joshi, developerWorks, December 2005): Learn about the Gang of Four design patterns at the heart of JavaServer Faces.
- "JSF for nonbelievers: Clearing the FUD about JSF" (Richard Hightower, developerWorks, February 2005): A four-part series that defends and demystifies JSF programming.
- Another Sleepless Night in Tucson:
Rick's blog, where he thinks aloud about JSF and Facelets (not to mention wine and cooking) till the wee hours of the morning.
-
The Java technology zone: Hundreds of articles about every aspect of Java programming.
Get products and technologies
- JSR 252: JavaServer Faces 1.2: Download
JavaServer Faces.
- Facelets home page: Download Facelets.
- Apache MyFaces project: Download the MyFaces Tomahawk extension.
Discuss
- developerWorks
blogs: Get involved in the developerWorks community!
Rick Hightower serves as chief technology officer for ArcMind Inc, a training company the specializes in JSF, Spring and Hibernate. He is coauthor of the popular book Java Tools for Extreme Programming, about applying extreme programming to J2EE development, and coauthor of Professional Struts.