My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

.

.

.

.

.

.

.

.


[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

miercuri, 30 decembrie 2015

JSF 2.2 [usage pitfall] - Exemplifying <c:if/> versus <ui:fragment/>

JSF novices use to confuse the JSF tag handlers with JSF component handlers. Basically, tag handlers will not reach the component tree and, as a consequence of this aspect, will not produce any markup (HTML markup). More details about tag handlers are available in Write a custom TagHandler skeleton. In addition, more details about component handlers are available in Writing a custom ComponentHandler skeleton.

A common scenario is to render a table data based on a <c:if> condition, as follows:

<h:dataTable value="#{playersBean.dataArrayList}" var="t">
 <h:column>
  <c:if test="#{t.age gt 26}">
   <h:outputText value="#{t.player}, #{t.age}"/>
  </c:if>
 </h:column>
</h:dataTable>

Well, the result will not be as expected. The problem is that <c:if> is a tag handler; therefore, it is efficiently reflected when the tree is built. A perfect workaround will be to replace <c:if> with the <ui:fragment> tag, which is a component handler. The rendered attribute of <ui:fragment> can successfully replace the <c:if> test using the following code:

<h:dataTable value="#{playersBean.dataArrayList}" var="t">
 <h:column>
  <ui:fragment rendered="#{t.age gt 26}">
   <h:outputText value="#{t.player}, #{t.age}"/>
  </ui:fragment>
 </h:column>
</h:dataTable>

Alternatively, in an even simpler way, use the rendered attribute of <h:outputText>; this approach is particular to this example:

<h:dataTable value="#{playersBean.dataArrayList}" var="t">
 <h:column>
  <h:outputText value="#{t.player}, #{t.age}" rendered="#{t.age gt 26}"/>
 </h:column>
</h:dataTable>

Instead, even cooler, using a lambda expression (EL 3.0), you can write the following code:

<h:dataTable value="#{(playersBean.dataArrayList.stream().
                       filter((p)->p.age gt 26 )).toList()}" var="t">
 <h:column>
  <h:outputText value="#{t.player}, #{t.age}"/>
 </h:column>
</h:dataTable>

Is very possible that lambda expression can help you to easily choose when and what to render.

marți, 29 decembrie 2015

JSF 2.2 [usage pitfall] - the view scoped beans and the stateless feature

In a stateless environment, the view scoped beans act as request scoped beans. Besides the fact that you can't create/manipulate views dynamically, this is one of the big disadvantages that comes with the stateless feature, because it will affect AJAX-based applications that usually use view scoped beans. You can easily test this behavior with a set of beans with different scopes. The view scoped bean can be defined as follows:

@Named
@ViewScoped
public class TimestampVSBean implements Serializable{

 private static final long serialVersionUID = 1L;

 private Timestamp timestamp;

 public TimestampVSBean() {
  java.util.Date date = new java.util.Date();
  timestamp = new Timestamp(date.getTime());
 }

 public Timestamp getTimestamp() {
  return timestamp;
 }

 public void setTimestamp(Timestamp timestamp) {
 this.timestamp = timestamp;
 }
}

Just change the scope to request, session, and application to obtain the other three beans.
Next, we will write a simple stateless view as follows:

<f:view transient="true">
 <h:form>
  <h:commandButton value="Generate Timestamp"/>
 </h:form>

 Request Scoped Bean:
 <h:outputText value="#{timestampRSBean.timestamp}"/>

 View Scoped Bean:
 <h:outputText value="#{timestampVSBean.timestamp}"/>
 [keep an eye on this in stateless mode]

 Session Scoped Bean:
 <h:outputText value="#{timestampSSBean.timestamp}"/>

 Application Scoped Bean:
 <h:outputText value="#{timestampASBean.timestamp}"/>
</f:view>

Afterwards, just submit this form several times (click on the Generate Timestamp button) and notice that the timestamp generated by the view scoped bean changes at every request. This is a JSF pitfall!

The request, session, and application scopes work as expected!

miercuri, 23 decembrie 2015

[JSF Page Author Beginner's Guide] JSF <f:attribute/>

The <f:attribute/> sets the specified name and attribute on the parent UIComponent. 
If the  "value" specified is not a literal, it will instead set the ValueExpression on the UIComponent.

Common/basic usage in JSF (I) - assigning supported attributes (e.g. value and style) to a JSF component (e.g. <h:outputText/>) (attributes values are hard coded):

<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://xmlns.jcp.org/jsf/html"
 xmlns:f="http://xmlns.jcp.org/jsf/core">
 <h:head>
  <title>JSF f:attribute examples</title>
 </h:head>
 <h:body>
  <h:outputText>
   <f:attribute name="value" value="Hello world!"/>
   <f:attribute name="style" value="color:blue"/>
  </h:outputText> 
 </h:body>
</html>

Common/basic usage in JSF (II) - assigning supported attributes (e.g. value and style) to a JSF component (e.g. <h:outputText/>) (attributes values are served from a properties file):

<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://xmlns.jcp.org/jsf/html"
 xmlns:f="http://xmlns.jcp.org/jsf/core">
 <h:head>
  <title>JSF f:attribute examples</title>
 </h:head>
 <h:body>
  <h:outputText>
   <f:attribute name="value" value="#{msg['HELLO']}"/>
   <f:attribute name="style" value="#{msg['STYLE']}"/>
  </h:outputText>
 </h:body>
</html>

The properties file has a two entries, as follows:

HELLO = Hello world!
STYLE = color:red

Common/basic usage in JSF (III) - Assigning supported attributes (e.g. value and style) to a JSF component (e.g. <h:outputText/>) (attributes values are served by a managed bean):

<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://xmlns.jcp.org/jsf/html"
 xmlns:f="http://xmlns.jcp.org/jsf/core">
 <h:head>
  <title>JSF f:attribute examples</title>
 </h:head>
 <h:body>
  <h:outputText>
   <f:attribute name="value" value="#{myBean.hello}"/>
   <f:attribute name="style" value="#{myBean.style}"/>
  </h:outputText>
 </h:body>
</html>

And the managed bean is:

package beans;

import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class MyBean {

 private final String hello = "Hello world!";
 private final String style = "color:green";

 public MyBean() {
 }

 public String getHello() {
  return hello;
 }

 public String getStyle() {
  return style;
 }  
}
The complete application is available here.

More examples:

Passing parameters or setting unsupported attributes to a JSF component

The <f:attribute> tag can be used to pass "parameters" or set unsupported attributes to a JSF component, as shown in the following code (obviously, the playerName and playerSurname are not command button supported attributes):

<h:commandButton actionListener="#{playersBean.parametersAction}">
 <f:attribute name="value" value="Send Rafael Nadal" />                           
 <f:attribute id="playerName" name="playerNameAttr" value="Rafael"/>              
 <f:attribute id="playerSurname" name="playerSurnameAttr" value="Nadal"/>              
</h:commandButton>

The PlayersBean is listed below (notice how the attributes are extracted on server):
package beans;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.faces.event.ActionEvent;
import javax.inject.Named;

@Named
@RequestScoped
public class PlayersBean {

 private final static Logger logger = Logger.getLogger(PlayersBean.class.getName());

 private String playerName;
 private String playerSurname;

 public PlayersBean() {
 }

 public String getPlayerName() {
  return playerName;
 }

 public void setPlayerName(String playerName) {
  this.playerName = playerName;
 }

 public String getPlayerSurname() {
  return playerSurname;
 }

 public void setPlayerSurname(String playerSurname) {
  this.playerSurname = playerSurname;
 }

 public void parametersAction(ActionEvent evt) {    
  playerName = (String) evt.getComponent().getAttributes().get("playerNameAttr");
  playerSurname = (String) evt.getComponent().getAttributes().get("playerSurnameAttr");

  logger.log(Level.INFO, "Name: {0} Surname: {1}", new Object[]{playerName, playerSurname});
 }
}

The complete application is available here.

Passing extra parameters to a custom validator

In the below example, you can see how <f:attribute/> can be used to pass extra parameters to a custom validator:

<h4>Select your next opponent:</h4>
<h:form id="racquetForm">
 <h:messages/>
 <h:selectOneMenu validator="gameValidator">
  <f:selectItem itemValue="Rafael Nadal" itemLabel="Rafael Nafal"/>                                   
  <f:selectItem itemValue="Roger Federer" itemLabel="Roger Federer"/>                                  
  <f:selectItem itemValue="Novak Djokovic" itemLabel="Novak Djokovic"/>                   
  <f:attribute name="msgAttrRafaelNadal" value="'King of clay' cannot be selected for this game ..."/>           
  <f:attribute name="msgAttrRogerFederer" value="'The Maestro' cannot be selected for this game ..."/>
  <f:attribute name="msgAttrNovakDjokovic" value="'Nole' cannot be selected for this game ..."/>
 </h:selectOneMenu>
 <h:commandButton value="Select"/>
</h:form>

And, the custom validator is listed below:

package beans;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

@FacesValidator
public class GameValidator implements Validator {

 @Override
 public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
  String player = value.toString().replaceAll("\\s+", "");
  throw new ValidatorException(new FacesMessage("Response:" + (String) component.getAttributes().get("msgAttr" + player)));
 }
}

The complete application is available here.

Validating multiple fields (cross-field) using a custom validator

In the below example, you can see how <f:attribute/> can be used to validate multiple fields using a custom validator:

<h4>Use 'f:attribute' to validate multiple components</h4>
<h:form id="registerForm">                       
 <h:panelGrid id="pgId" columns="3">
  <h:outputLabel for="nameId" value="Player name : " />
  <h:inputText id="nameId" value="#{playersBean.name}" required="true" />
  <h:message for="nameId" style="color: red;" />
  <h:outputLabel for="surnameId" value="Player surname : " />
  <h:inputText id="surnameId" value="#{playersBean.surname}" required="true" />
  <h:message for="surnameId" style="color: red;" />
  <h:outputLabel for="bankAccountId" value="Bank account : " />
  <h:inputText id="bankAccountId" value="#{playersBean.bank}" required="true">
   <f:validator validatorId="bankValidator" />
   <f:attribute name="confirmBankAccountAttr" value="#{confirmBankAccount}" />
  </h:inputText>
  <h:message for="bankAccountId" style="color: red;" />
  <h:outputLabel for="confirmBankAccountId" value="Confirm bank account : " />
  <h:inputText id="confirmBankAccountId" value="#{playersBean.cbank}"
               binding="#{confirmBankAccount}" required="true" />
  <h:message for="confirmBankAccountId" style="color: red;" />
 </h:panelGrid>
 <h:commandButton action="done" value="Send" />
</h:form>

And, the custom validator is listed below:

package beans;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

@FacesValidator
public class BankValidator implements Validator {

 @Override
 public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {

  String bankAccount = value.toString();

  UIInput uiInputBankAccount = (UIInput) component.getAttributes().get("confirmBankAccountAttr");
  String bankAccountC = uiInputBankAccount.getSubmittedValue().toString();

  if ((bankAccount != null) && (bankAccountC != null)) {
       if (!bankAccount.equals(bankAccountC)) {
           uiInputBankAccount.setValid(false);
           throw new ValidatorException(new FacesMessage("Bank account must match bank account confirmation!"));
       }
  }
 }
}

And, the managed bean is:

package beans;

import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class PlayersBean {

 private String name;
 private String surname;
 private String bank;
 private String cbank;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getSurname() {
  return surname;
 }

 public void setSurname(String surname) {
  this.surname = surname;
 }

 public String getBank() {
  return bank;
 }

 public void setBank(String bank) {
  this.bank = bank;
 }

 public String getCbank() {
  return cbank;
 }

 public void setCbank(String cbank) {
  this.cbank = cbank;
 }
}

The complete application is available here.

Note When you have to deal with cross-field validation, you may want to consider the OmniFaces <o:validateBean/> or the new JSF 2.3 <f:validateWholeBean/>.

Dynamically passing parameters

Another case when the <f:attribute/> tag can be useful is when dynamically passing parameters in conjunction with UI components bound to the managed bean using the binding attribute. This is very useful, especially because there is no solution provided by JSF for passing parameters to the getters/setters methods of the bound UI components, as shown in the following code:

<h:form>           
 <h:inputText binding="#{playersBean.htmlInputText}" value="#{playersBean.playerNameSurname}">
  <f:attribute name="playerNameAttr" value="Rafael Nadal"/>
 </h:inputText>                     
</h:form>

And the managed bean is:

package beans;

import javax.enterprise.context.RequestScoped;
import javax.faces.component.UIInput;
import javax.inject.Named;

@Named
@RequestScoped
public class PlayersBean {

 private UIInput htmlInputText= null;   
  
 public PlayersBean() {
 }

 public UIInput getHtmlInputText() {
  return htmlInputText;
 }

 public void setHtmlInputText(UIInput htmlInputText) {
  this.htmlInputText = htmlInputText;
 }   
   
 public String getPlayerNameSurname() {
  return (String) htmlInputText.getAttributes().get("playerNameAttr");
 }   
}

Now, the value of the <h:inputText/> tag should contain the value set via the <f:attribute/> tag. Be careful to use only unique names for the attributes and to not interfere (try to overwrite) with the default attributes of the UI component.

The complete application is available here.

Passing extra parameters to PrimeFaces FileUpload component

If you are a fan of PrimeFaces, then you will probably find the next example useful. One of the greatest built-in components of PrimeFaces is the <p:fileUpload/> tag, which can be used, obviously, to upload files. Sometimes, besides the files that will be uploaded, you need to pass some extra parameters, for example, the files' owner name and surname. Well, the <p:fileUpload/> tag doesn't come with a solution for this, but the <f:attribute/> tag can be helpful. The following is the code of a classic <p:fileUpload/> tag with the <f:attribute/> tag:

<h:form> 
 <p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
                mode="advanced" dragDropSupport="false" 
                update="messages" fileLimit="3" allowTypes="/(\.|\/)(gif|jpe?g|png)$/"> 
  <f:attribute id="playerName" name="playerNameAttr" value="Rafael"/>               
  <f:attribute id="playerSurname" name="playerSurnameAttr" value="Nadal"/>
 </p:fileUpload>
 <p:growl id="messages" showDetail="true"/> 
</h:form>

The handleFileUpload() method is responsible for the upload-specific steps (skipped in the following code), but it can also access the values passed by the <f:attribute/> tag:

package beans;

import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Named;

import org.primefaces.event.FileUploadEvent;

@Named
@RequestScoped
public class FileUploadController {

 public void handleFileUpload(FileUploadEvent evt) {
  String playerName = (String) evt.getComponent().getAttributes().get("playerNameAttr");
  String playerSurname = (String) evt.getComponent().getAttributes().get("playerSurnameAttr");
  FacesMessage msg = new FacesMessage("Succesful", evt.getFile().getFileName() + " is uploaded for " + playerName + " " + playerSurname);
  FacesContext.getCurrentInstance().addMessage(null, msg);
 }
}
The complete application is available here.

Set a component renderer from page

Let's suppose that we have two input components as below:

<h:inputText id="..." value="..."/>
<h:inputText id="..." value="... "/>

By default, both of them will be renderer via the renderer identified by the javax.faces.Text renderer type. But, let's suppose that we want to use the default renderer for the first input component, and a custom renderer (provided by us with renderer type, dummy.foo.Text) for the second input component. Well, we can try to use the rendererType attribute, but you will notice that this doesn't work.

<h:inputText id="..." value="..."/>
<h:inputText id="..." value="... " rendererType="dummy.foo.Text"/>

Bauke Scholtz (aka BalusC), member of JSF EG , explains: "It will work. Tooling only doesn't recognize is because it's not listed in taglib file which the tooling depends on. You can alternatively use f:attribute for that."

So, until this will work, we can use <f:attribute/> as below:

<h:inputText id="..." value="..."/>
<h:inputText id="..." value="...">        
 <f:attribute name="rendererType" value="dummy.foo.Text"/>
</h:inputText>

Using a navigation case to enter in a flow

When you need to use a navigation case to enter in a flow, you will have to specify the <to-flow-document-id>document_ID</toflow-document-id> statement nested in the <navigation-case/> tag. If there is no document ID, that uses <to-flow-document-id/>. Moreover a <h:button/> (or <h:link/>) can be used to enter in such a flow, as follows:

<h:button id="..." value="enter flow" outcome="flow">
 <f:attribute name="to-flow-document-id" value="unique"/>
</h:button>

If you choose to write a programmatic navigation case, then JSF 2.2 comes with a method named, getToFlowDocumentId(), which should be overridden for indicating the document ID.

See also Mkyong.com.
More resources on Constantin Alin, ZEEF page.

duminică, 20 decembrie 2015

JSF 2.3 align the <f:convertDateTime/> to the new data and time classes in JDK 8 (JSR 310)

by Constantin Alin

JSF 2.3 will have Java 8 as a minimum dependency. One of the affected artifacts is the <f:convertDateTime/> built-in converter which has been updated in order to support new types for the type attribute, and, depending on the value of the type attribute, the converter will use the java.text.SimpleDateFormat or java.time.format.DateTimeFormatter class for formatting.

Note When the converter type attribute value is date, time or both, JSF (2.2 and 2.3) uses the java.text.SimpleDateFormat class. Starting with JSF 2.3, when the converter type attribute value is localDate, localTime, localDateTime, offsetTime, offsetDateTime or zonedDateTime, the java.time.format.DateTimeFormatter class will be used.

Note In both, JSF 2.2 and JSF 2.3, if the pattern attribute is specified, the dateStyle or timeStyle attributes are not taken in consideration during the parsing and formatting process.

In JSF 2.2, the type attribute can have only one of the date (default value that relies on DateFormat.getDateTimeInstance(dateStyle) when pattern is missing), time (relies on DateFormat.getDateTimeInstance(timeStyle)when pattern is missing) or both (relies on DateFormat.getDateTimeInstance(dateStyle, timeStyle) when pattern is missing) values. Let’s take a look at the following examples specific to JSF 2.2.

Using type="date" (default value) and the dateStyle attribute to format Date:

<h:form>
 <h:inputText value="#{myBean.date}">
  <f:convertDateTime pattern="dd-MM-yyyy" />
 </h:inputText>
</h:form>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime dateStyle="short" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime dateStyle="medium" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime dateStyle="long" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime dateStyle="full" />
</h:outputText>

 Using type="time" and the timeStyle attribute to format Date:

<h:form>
 <h:inputText value="#{myBean.date}">
  <f:convertDateTime type="time" pattern="h:mm" />
 </h:inputText>
</h:form>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime type="time" timeStyle="short" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime type="time" timeStyle="medium" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime type="time" timeStyle="long" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime type="time" timeStyle="full" />
</h:outputText>

Using type="both" and the dateStyle and timeStyle attributes to format Date:

<h:form>
 <h:inputText value="#{myBean.date}">
  <f:convertDateTime type="both" pattern="dd-MM-yyyy, h:mm" />
 </h:inputText>
</h:form>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime type="both" dateStyle="short" timeStyle="short" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime type="both" dateStyle="medium" timeStyle="medium" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime type="both" dateStyle="long" timeStyle="long" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime type="both" dateStyle="full" timeStyle="full" />
</h:outputText>


Using the pattern attribute to format Date:

<h:outputText value="#{myBean.date}">
 <f:convertDateTime pattern="H:mm" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime pattern="h:mm a" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime pattern="H:mm:ss:SSS" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime pattern="K:mm a,z" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime pattern="dd.MM.yy" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime pattern="EEE, MMM d, ''yy" />
</h:outputText>

<h:outputText value="#{myBean.date}">
 <f:convertDateTime pattern="yyyy.MMMMM.dd GGG hh:mm aaa" />
</h:outputText/>


<h:outputText value="#{myBean.date}">
 <f:convertDateTime pattern="yyyy.MM.dd G 'at' hh:mm:ss z" />
</h:outputText>


The managed bean behind the above examples is listed below:

@Named
@RequestScoped
public class MyBean {

 private Date date;

 public MyBean() {
  date = new Date();
 }

 // getter and setter
}

The upcoming JSF 2.3 now adds new possible values for the type attribute, as follows:

·         localDate, localTime, localDateTime, offsetTime, offsetDateTime, zonedDateTime

To see the new possible values in action, suppose we have the following managed bean:

@Named
@RequestScoped
public class MyBean {

 private LocalDate localDate;
 private LocalTime localTime;
 private LocalDateTime localDateTime;
 private OffsetTime offsetTime;
 private OffsetDateTime offsetDateTime;
 private ZonedDateTime zonedDateTime;

 public MyBean() {
  localDate = LocalDate.now();
  localTime = LocalTime.now();
  localDateTime = LocalDateTime.now();
  offsetTime = OffsetTime.now();
  offsetDateTime = OffsetDateTime.now();
  zonedDateTime = ZonedDateTime.now();
 }

 // getters and setters
}

To format an OffsetTime we can use type="offsetTime" with the pattern attribute as follows:

<h:form>
 <h:inputText value="#{myBean.offsetTime}">
  <f:convertDateTime type="offsetTime" pattern="H:mm" />
 </h:inputText>
</h:form>

<h:outputText value="#{myBean.offsetTime}">
 <f:convertDateTime type="offsetTime" pattern="h:mm a" />
</h:outputText>

<h:outputText value="#{myBean.offsetTime}">
 <f:convertDateTime type="offsetTime" pattern="H:mm:ss:SSS" />
</h:outputText>


To format an OffsetDateTime we can use type="offsetDateTime" with the pattern attribute as follows:

<h:form>
 <h:inputText value="#{myBean.offsetDateTime}">
  <f:convertDateTime type="offsetDateTime" pattern="dd-MM-yyyy, h:mm" />
 </h:inputText>
</h:form>

<h:outputText value="#{myBean.offsetDateTime}">
 <f:convertDateTime type="offsetDateTime" pattern="h:mm a" />
</h:outputText>

<h:outputText value="#{myBean.offsetDateTime}">
 <f:convertDateTime type="offsetDateTime" pattern="H:mm:ss:SSS" />
</h:outputText>

<h:outputText value="#{myBean.offsetDateTime}">
 <f:convertDateTime type="offsetDateTime" pattern="dd.MM.yy" />
</h:outputText>

<h:outputText value="#{myBean.offsetDateTime}">
 <f:convertDateTime type="offsetDateTime" pattern="EEE, MMM d, ''yy" />
</h:outputText>


To format a ZonedDateTime we can use type="zonedDateTime" with the pattern attribute as follows:

<h:form>
 <h:inputText value="#{myBean.zonedDateTime}">
  <f:convertDateTime type="zonedDateTime" pattern="dd-MM-yyyy, h:mm" />
 </h:inputText>
</h:form>

<h:outputText value="#{myBean.zonedDateTime}">
 <f:convertDateTime type="zonedDateTime" pattern="h:mm a" />
</h:outputText>

<h:outputText value="#{myBean.zonedDateTime}">
 <f:convertDateTime type="zonedDateTime" pattern="H:mm:ss:SSS" />
</h:outputText>

<h:outputText value="#{myBean.zonedDateTime}">
 <f:convertDateTime type="zonedDateTime" pattern="dd.MM.yy" />
</h:outputText>

<h:outputText value="#{myBean.zonedDateTime}">
 <f:convertDateTime type="zonedDateTime" pattern="EEE, MMM d, ''yy" />
</h:outputText>

<h:outputText value="#{myBean.zonedDateTime}">
 <f:convertDateTime type="zonedDateTime" pattern="yyyy.MM.dd G 'at' hh:mm:ss z" />
</h:outputText>


Note When type is offsetTime, offsetDateTime or zonedDateTime, the implementation of <f:convertDateTime/> ignores the dateStyle or timeStyle attributes and 
returns the default formatted value or the value formatted by the pattern attribute, if specified.

To format a LocalDate we can use type="localDate" and the dateStyle attribute:

<h:form>
 <h:inputText value="#{myBean.localDate}">
  <f:convertDateTime type="localDate" pattern="dd-MM-yyyy" />
 </h:inputText>
</h:form>

<h:outputText value="#{myBean.localDate}">
 <f:convertDateTime type="localDate" dateStyle="short" />
</h:outputText>

<h:outputText value="#{myBean.localDate}">
 <f:convertDateTime type="localDate" dateStyle="medium" />
</h:outputText>

<h:outputText value="#{myBean.localDate}">
 <f:convertDateTime type="localDate" dateStyle="long" />
</h:outputText>

<h:outputText value="#{myBean.localDate}">
 <f:convertDateTime type="localDate" dateStyle="full" />
</h:outputText>


The complete application is available here.

Some issues in JSF 2.3-m04

1.  The converter won’t work with type="localTime" and timeStyle. This seems to be a typo in source code, the dateStyle is used instead of timeStyle:

// Mojarra 2.3-m04 source code
public class DateTimeConverter implements Converter, PartialStateHolder {

 ...
 private String dateStyle = "default";
 private String timeStyle = "default";
 ...

 private FormatWrapper getDateFormat(Locale locale) {        
  ...
  } else if (type.equals("localTime")) {
     dtf = (null != pattern) ? DateTimeFormatter.ofPattern(pattern, locale) :
            DateTimeFormatter.ofLocalizedTime(getFormatStyle(dateStyle));
     from = LocalTime::from;
     ...
  }
  ...
}

As a consequence, you can write <f:convertDateTime type="localTime" dateStyle="short" /> and it will work.

2. You can’t format only time when type="localDateTime":

Consider the following example:

<h:outputText value="#{myBean.localDateTime}">
 <f:convertDateTime type="localDateTime" timeStyle="short" />
</h:outputText>

Similar to first issue, the value of the dateStyle attribute will be used instead of timeStyle.

As a consequence, you can’t "combine" timeStyle and dateStyle when type="localDateTime". A LocalDateTime object holds both date and time. So we should be able to format the date part with one style and the time part with another style. For example:

<h:outputText value="#{myBean.localDateTime}">
 <f:convertDateTime type="localDateTime" dateStyle="medium" timeStyle="short" />
</h:outputText>

This will not work as expected. During the parsing and formatting process, the timeStyle attribute will be ignored. Only dateStyle will be used:

// Mojarra 2.3-m04 source code
public class DateTimeConverter implements Converter, PartialStateHolder {

 ...
 private String dateStyle = "default";
 private String timeStyle = "default";
 ...

 private FormatWrapper getDateFormat(Locale locale) {
  ...
  } else if (type.equals("localDateTime")) {
     dtf = (null != pattern) ? DateTimeFormatter.ofPattern(pattern, locale) :
            DateTimeFormatter.ofLocalizedDateTime(getFormatStyle(dateStyle));
     from = LocalDateTime::from;
  }
  ...
}

The fix may consist in calling the DateTimeFormatter.ofLocalizedDateTime() method with two parameters, something like DateTimeFormatter.ofLocalizedDateTime(getFormatStyle(dateStyle), getFormatStyle(timeStyle)).

3. Notice that you can’t use the converter with type="localTime" or type="localDateTime" and dateStyle or timeStyle with a value of long or full. This will cause an javax.faces.convert.ConverterException exception which "hides" the "original" exception caused by the JDK-8085887 bug. Although the bug only mentions LocalDateTime, the same happens when using LocalTime.

Further reading:
JDK 8 time support in f:convertDateTime

JSF BOOKS COLLECTION

Postări populare

Visitors Starting 4 September 2015

Locations of Site Visitors