In the following sections, we cover the converters and validators that are a part of the JSF library. Later in this chapter, you learn how to supply your own validation code if your needs go beyond the basics. Conversion of Numbers and Dates A web application stores data of many types, but the web user interface deals exclusively with strings. For example, suppose the user needs to edit a Date object that is stored in the business logic. First, the Date object is converted to a string that is sent to the client browser to be displayed inside a textfield. The user then edits the textfield. The resulting string is returned to the server and must be converted back to a Date object. The same situation holds, of course, for primitive types such as int, double, or boolean. The user of the web application edits strings, and the JSF container needs to convert the string to the type required by the application. To see a typical use of a built-in converter, imagine a web application that is used to process payments (see Figure 6-2). The payment data includes Figure 6-2. Processing payments We attach a converter to the textfield and tell it to format the current value with at least two digits after the decimal point:
<h:inputText value="#{payment.amount}"> <f:convertNumber minFractionDigits="2"/> </h:inputText> The f:convertNumber converter is one of the standard converters supplied by the JSF implementation. The second field in this screen does not use a converter. (Later in this chapter, we attach a custom converter.) The third field uses an f:datetime converter whose pattern attribute is set to the string MM/yyyy. (The pattern string format is documented in the API documentation for the java.text.SimpleDateFormat class.)
<h:inputText value="#{payment.date}"> <f:convertDateTime pattern="MM/yyyy"/> </h:inputText> In the result.jsp page, we show the inputs that the user provided, using a different converter for the payment amount:
<h:outputText value="#{payment.amount}"> <f:convertNumber type="currency"/> </h:outputText> This converter automatically supplies a currency symbol and decimal separators (see Figure 6-3). Figure 6-3. Displaying the Payment Information Converters and Attributes Tables 6-1 and 6-2 show the standard converters and their attributes. Table 6-1. Attributes of the f:convertNumber tagAttribute | Type | Value |
---|
type | String | number (default), currency, or percent | pattern | String | Formatting pattern, as defined in java.text.DecimalFormat | xaxFractionDigits | int | Maximum number of digits in the fractional part | minFractionDigits | int | Minimum number of digits in the fractional part | maxIntegerDigits | int | Maximum number of digits in the integer part | minIntegerDigits | int | Minimum number of digits in the integer part | integerOnly | boolean | True if only the integer part is parsed (default: false) | groupingUsed | boolean | True if grouping separators are used (default: true) | locale | java.util.Locale | Locale whose preferences are to be used for parsing and formatting | currencyCode | String | ISO 4217 currency code to use when converting currency values | currencySymbol | String | Currency symbol to use when converting currency values |
Table 6-2. Attributes of the f:convertDateTime tagAttribute | Type | Value |
---|
type | String | Date (default), time, or both | dateStyle | String | default, short, medium, long, or full | timeStyle | String | default, short, medium, long, or full | pattern | String | Formatting pattern, as defined in java.text.SimpleDateFormat | locale | java.util.Locale | Locale whose preferences are to be used for parsing and formatting | timeZone | java.util.TimeZone | Time zone to use for parsing and formatting |
NOTE | If you use a value binding whose type is either a primitive type or BigInteger/BigDecimal, then you don't need to specify any converter. The JSF implementation automatically picks a standard converter. However, you need to specify an explicit converter for Date values. |
The converter Attribute An alternate syntax for attaching a converter to a component is to add the converter attribute to the component tag. You specify the ID of the converter like this:
<h:outputText value="#{payment.date}" converter="javax.faces.DateTime"/> This is equivalent to using f:convertDateTime with no attributes:
<h:outputText value="#{payment.date}"> <f:convertDateTime/> </h:outputText> All JSF implementations must define a set of converters with predefined IDs: javax.faces.DateTime (used by f:convertDateTime) javax.faces.Number (used by f:convertNumber) javax.faces.Boolean, javax.faces.Byte, javax.faces.Character, javax.faces.Double, javax.faces.Float, javax.faces.Integer, javax.faces.Long, javax.faces.Short (automatically used for primitive types and their wrapper classes) javax.faces.BigDecimal, javax.faces.BigInteger (automatically used for BigDecimal/BigInteger) Additional converter IDs can be configured in an application configuration file see page 226 for details. CAUTION | When the value of the converter attribute is a string, then the value indicates the ID of a converter. However, if it is a value binding expression, then its value must be a converter object an object of a class that implements the Converter interface. That interface is introduced on page 223. |
Conversion Errors When a conversion error occurs, the following actions are the result: The component whose conversion failed posts a message and declares itself invalid. (You see in the next section how to display the message.) The JSF implementation redisplays the current page immediately after the "Process Validations" phase has completed. This behavior is generally desirable. If a user provides an illegal input for, say, a field that requires an integer, then the web application should not try to use that illegal input. The JSF implementation automatically redisplays the current page, giving the user another chance to enter the value correctly. However, you should avoid overly restrictive conversion options for input fields. For example, consider the "amount" field in our example. Had we used a currency format, then the current value would have been nicely formatted. But suppose a user enters 100 (without a leading $ sign). The currency formatter will complain that the input is not a legal currency value. That's too strict for human use. To overcome this problem, you can program a custom converter. A custom converter can format a value prettily, yet be lenient when interpreting human input. Custom converters are described later in this chapter. TIP | When gathering input from the user, you should either use a lenient converter or simply redesign your form to be more user friendly. For example, rather than forcing users to format the expiration date as MM/dddd, you can supply two input fields, one for the month and another for the year. |
Displaying Error Messages Of course, it is important that the user be able to see the messages that are caused by conversion and validation errors. You should add h:message tags whenever you use converters and validators. Normally, you want to show the error messages next to the components that reported them (see Figure 6-4). Give an ID to the component, and reference that ID in the h:message tag.
<h:inputText value="#{payment.amount}"/> <h:message for="amount"/> Figure 6-4. Displaying a Conversion Error Message The h:message tag takes a number of attributes to describe the appearance of the message see Chapter 4 for details. Here, we discuss only the attributes that are of particular interest for error reporting. A message has two parts: summary and detail. By default, the h:message tag shows the detail and hides the summary. If you want to show the summary message instead, use these attributes:
<h:message for="amount" showSummary="true" showDetail="false"/> CAUTION | If you use a standard converter, display either the summary message or the detail message, but not both for some errors, the messages are identical. You don't want your users to ponder an error message that reads "Conversion error occurred. Conversion error occurred." |
You use the styleClass or style attribute to change the appearance of the error message:
<h:messages style/> or
<h:message for="amount" style="color:red"/> We recommend that you use styleClass and a stylesheet instead of a hard-coded style. Displaying All Error Messages It is uncommon to have multiple messages for one component, but it can happen. The h:message tag produces only the first message. Unfortunately, you don't know whether the first message is the most useful one for the user. While no tag shows all messages for a particular component, you can show a listing of all messages from all components with the h:messages tag. By default, the h:messages tag shows the message summary but not the message detail. This behavior is opposite from that of the h:message tag. For h:messages, you usually want to set the layout attribute to "table" so that the messages are lined up vertically. Otherwise they are simply concatenated.
<h:messages layout="table"/> TIP | Whenever you create a message, make sure it ends with a period and a space, to ensure a neat appearance when messages are concatenated. |
However, we find it difficult to believe that anyone would actually use the h:messages tag in a form with multiple input fields. Suppose a user happened to fill in two fields incorrectly. The h:messages tag would display two "Conversion error occurred" messages, with no indication of the offending fields. TIP | The h:messages tag is useful for debugging. Whenever your JSF application stalls at a particular page and is unwilling to move on, add a <h:messages/> tag to see if a failed conversion or validation is the culprit. |
Changing the Text of Standard Error Messages As you saw in Figure 6-4, the default error message for a conversion error is "Conversion error occurred." If you think that your audience is unfamiliar with the concept of data conversion, you may want to change this message. Set up a message bundle, as explained in Chapter 2. Add the replacement message, using the key javax.faces.component.UIInput.CONVERSION. For example,
javax.faces.component.UIInput.CONVERSION=Please correct your input. Then set the base name of the bundle in a configuration file (such as faces-config.xml):
<faces-config> <application> <message-bundle>com.corejsf.messages</message-bundle> </application> ... </faces-config> A Complete Converter Example We are now ready for our first complete example. Figure 6-5 shows the directory structure of the application. This web application simply asks the user to supply payment information (Listing 6-1), and then displays the formatted information on a confirmation screen (Listing 6-2). The messages are in Listing 6-3 and the bean class is in Listing 6-4. Listing 6-1. converter/index.jsp 1. <html> 2. <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 3. <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> 4. <f:view> 5. <head> 6. <link href="styles.css" rel="stylesheet" type="text/css"/> 7. <f:loadBundle basename="com.corejsf.messages" var="msgs"/> 8. <title><h:outputText value="#{msgs.title}"/></title> 9. </head> 10. <body> 11. <h:form> 12. <h1><h:outputText value="#{msgs.enterPayment}"/></h1> 13. <h:panelGrid columns="3"> 14. <h:outputText value="#{msgs.amount}"/> 15. <h:inputText value="#{payment.amount}"> 16. <f:convertNumber minFractionDigits="2"/> 17. </h:inputText> 18. <h:message for="amount" style/> 19. 20. <h:outputText value="#{msgs.creditCard}"/> 21. <h:inputText value="#{payment.card}"/> 22. <h:panelGroup/> 23. 24. <h:outputText value="#{msgs.expirationDate}"/> 25. <h:inputText value="#{payment.date}"> 26. <f:convertDateTime pattern="MM/yyyy"/> 27. </h:inputText> 28. <h:message for="date" style/> 29. </h:panelGrid> 30. <h:commandButton value="#{msgs.process}" action="process"/> 31. </h:form> 32. </body> 33. </f:view> 34. </html> Listing 6-2. converter/result.jsp 1. <html> 2. <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 3. <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> 4. <f:view> 5. <head> 6. <link href="styles.css" rel="stylesheet" type="text/css"/> 7. <f:loadBundle basename="com.corejsf.messages" var="msgs"/> 8. <title><h:outputText value="#{msgs.title}"/></title> 9. </head> 10. <body> 11. <h:form> 12. <h1><h:outputText value="#{msgs.paymentInformation}"/></h1> 13. <h:panelGrid columns="2"> 14. <h:outputText value="#{msgs.amount}"/> 15. <h:outputText value="#{payment.amount}"> 16. <f:convertNumber type="currency"/> 17. </h:outputText> 18. 19. <h:outputText value="#{msgs.creditCard}"/> 20. <h:outputText value="#{payment.card}"/> 21. 22. <h:outputText value="#{msgs.expirationDate}"/> 23. <h:outputText value="#{payment.date}"> 24. <f:convertDateTime pattern="MM/yyyy"/> 25. </h:outputText> 26. </h:panelGrid> 27. <h:commandButton value="Back" action="back"/> 28. </h:form> 29. </body> 30. </f:view> 31. </html> Listing 6-3. converter/WEB-INF/classes/com/corejsf/messages.properties 1. title=An Application to Test Data Conversion 2. enterPayment=Please enter the payment information: 3. amount=Amount: 4. creditCard=Credit Card: 5. expirationDate=Expiration date (Month/Year): 6. process=Process 7. paymentInformation=Payment information Listing 6-4. converter/WEB-INF/classes/com/corejsf/PaymentBean.java 1. package com.corejsf; 2. 3. import java.util.Date; 4. 5. public class PaymentBean { 6. private double amount; 7. private String card = ""; 8. private Date date = new Date(); 9. 10. // PROPERTY: amount 11. public void setAmount(double newValue) { amount = newValue; } 12. public double getAmount() { return amount; } 13. 14. // PROPERTY: card 15. public void setCard(String newValue) { card = newValue; } 16. public String getCard() { return card; } 17. 18. // PROPERTY: date 19. public void setDate(Date newValue) { date = newValue; } 20. public Date getDate() { return date; } 21. } Figure 6-5. Directory Structure of the Converter Sample |