Skip to main content

A Case Study of Custom JSF Converters - Automatically Converting Years With 2 Digits Into 4 Digits


You can find the demonstrated code of this post on my Github repo here.

The user story

" As a banker, I want to enter a client's birthday like 01.01.80 or 01.01.1980,
So that the birthday can be displayed as 01.01.1980 "

Implementation

Firstly, I thought about how to use a built-in converter likes the following.

<h:inputText id="birthdate" 
 value="#{data.birthdate}" 
 type="date" >
 <f:convertDateTime/> 
 <f:ajax event="change" 
   listener="#{data.onCalculate}" 
   execute="@this"
   render="@this" />
</h:inputText>

However, without defining a pattern, JSF used its default one which was not my desire. It threw an exception when I tried to enter a date like "01.01.90".

> myform:birthdate: '01.01.90' could not be understood as a date. Example: Mar 4, 2018 

Actually, I even could not define either pattern "dd.MM.yyyy" or "dd.MM.yy" for "f:convertDateTime":
- "dd.MM.yyyy" --> 01.01.80 becomes 01.01.0080
- "dd.MM.yy" --> 01.01.1980 becomes 01.01.80

Well, this was a case for us to define our own custom converter.
- XHTML code
<h:inputText id="birthdate" 
 value="#{data.birthdate}" 
 type="date">
 <f:converter converterId="myDateTimeConverter" /> 
 <f:ajax event="change" 
   listener="#{data.onCalculate}" 
   execute="@this"
   render="@this" />
</h:inputText>

- Java code
package vn.nvanhuong.javalab.jsf.dateformat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;

import org.apache.commons.lang3.StringUtils;

@FacesConverter("myDateTimeConverter")
public class DateTimeConverter implements Converter {

 private static final String DD_MM_YY = "dd.MM.yy";
 private static final String DD_MM_YYYY = "dd.MM.yyyy";
 
 public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
  if (StringUtils.isEmpty(value)) {
   return null;
  }

  SimpleDateFormat formatter = new SimpleDateFormat(DD_MM_YY);
  Date parsedObject = null;
  try {
   parsedObject = formatter.parse(value);
  } catch (ParseException e) {
   throw new ConverterException(e);
  }
  return parsedObject;
 }
 
 public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
  if (value == null) {
   return null;
  }

  SimpleDateFormat formatter = new SimpleDateFormat(DD_MM_YYYY);
  return formatter.format(value);
 }

}

The idea was that I allowed users to enter the year with 4 or 2 digits. Then, I used SimpleDateFormatter to convert the entered value into a value with type Date at JSF's phase "Apply Request" which calls method "getAsObject".

The cool thing here was with pattern "dd.MM.yy" the formatter allowed us to do that! Then, we always displayed the date with format "dd.MM.yyyy" at JSF's phase "Render Response" which calls method "getAsString".

Comments

Popular posts from this blog

How to convert time between timezone in Java, Primefaces?

I use the calendar Primefaces component with timeOnly and timeZone attributes for using only hour format (HH:mm). Like this: <p:calendar id="xabsOvertimeTimeFrom" pattern="HH:mm" timeOnly="true" value="#{data.dateFrom}" timeZone="#{data.timeZone}"/> We can convert the value of #{data.dateFrom} from GMT/UTC time zone to local, conversely, from local time zone to GMT/UTC time zone. Here is my functions: package vn.nvanhuong.timezoneconverter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; public class TimeZoneConverter { /** * convert a date with hour format (HH:mm) from local time zone to UTC time zone */ public static Date convertHourToUTCTimeZone(Date inputDate) throws ParseException { if(inputDate == null){ return null; } Calendar calendar = Calendar.getInstance(); calendar.setTime(inputDate); int ...

JSF, Primefaces - Invoking Application Code Even When Validation Failed

A use case I have a form which has requirements as follow: - There are some mandatory fields. - Validation is triggered when changing value on each field. - A button "Next" is enable only when all fields are entered. It turns to disabled if any field is empty. My first approach I defined a variable "isDisableNext" at a backend bean "Controller" for dynamically disabling/enabling the "Next" button by performing event "onValueChange", but, it had a problem: <h:form id="personForm"> <p:outputLabel value="First Name" for="firstName"/> <p:inputText id="firstName" value="#{person.firstName}" required="true"> <p:ajax event="change" listener="#{controller.onValueChange}" update="nextButton"/> </p:inputText> <p:outputLabel value="Last Name" for="lastName"/> <p:i...

Styling Sort Icons Using Font Awesome for Primefaces' Data Table

So far, Primefaces has used image sprites for displaying the sort icons. This leads to a problem if we want to make a different style for these icons; for example, I would make the icon "arrow up" more blurry at the first time the table loading because I want to highlight the icon "arrow down". I found a way that I can replace these icons with Font Awesome icons. We will use "CSS Pseudo-classes" to achieve it. The hardest thing here is that we should handle displaying icons in different cases. There is a case both "arrow up" and "arrow down" showing and other case is only one of these icons is shown. .ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s { background-image: none; margin-left: 5px; font-size: 1.1666em; position: relative; } .ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s:not(.ui-icon-triangle-1-s)::before { content: "\f106"; font-family: "FontAwesome"; position: ...

Attribute 'for' of label component with id xxxx is not defined

I got the warning in the log file when I have used the tag <h:outputLabel> without attribute " for " in xhtml file. It was really polluting my server log files. The logged information actually makes sense anyway! We could find an answer as the following: "Having h:outputLabel without a "for" attribute is meaningless. If you are not attaching the label, you should be using h:outputText instead of h:outputLabel." However, these solutions are not possible just for my situation. Instead of using h:outputText for only displaying text, my team has used h:outputLabel too many places. We were nearly in our release time (next day) so it is quite risky and takes much efforts if we try to correct it. Because the style (with CSS) is already done with h:ouputLabel . The alternative by adding attribute " for " the existing h:outputLabel is not reasonable either. I really need to find another solution. Fortunately, I came across a way if I cha...

Junit - Test fails on French or German string assertion

In my previous post about building a regex to check a text without special characters but allow German and French . I met a problem that the unit test works fine on my machine using Eclipse, but it was fail when running on Jenkins' build job. Here is my test: @Test public void shouldAllowFrenchAndGermanCharacters(){ String source = "ÄäÖöÜüß áÁàÀâÂéÉèÈêÊîÎçÇ"; assertFalse(SpecialCharactersUtils.isExistSpecialCharater(source)); } Production code: public static boolean isExistNotAllowedCharacters(String source){ Pattern regex = Pattern.compile("^[a-zA-Z_0-9_ÄäÖöÜüß áÁàÀâÂéÉèÈêÊîÎçÇ]*$"); Matcher matcher = regex.matcher(source); return !matcher.matches(); } The result likes the following: Failed tests: SpecialCharactersUtilsTest.shouldAllowFrenchAndGermanCharacters:32 null A guy from stackoverflow.com says: "This is probably due to the default encoding used for your Java source files. The ö in the string literal in the J...