Thursday, July 19, 2012

Compare two dates in two <rich:calendar> components

In this lesson Im going to show you how to compare the dates of two &lt;rich:calendar> components in a JSF page.

For an example think that you are creating a JSF page to enter employee details.
In that page you have two calendar components. One is to enter the join date and the other one is to enter the retired date of the employee.
So obviously join date should be earlier than the retired date. How do you validate whether join date is earlier than retired date and show a rich:message if validation failed?

1) Method 1 (For JSF users)
If you are not using JBoss SEAM, this method is for you. (Its obvious that SEAM users also can use this method.)

First, you have to create a validator class as below.


package inova;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;


public class DateComparator implements Validator {
public void validate(FacesContext context, UIComponent component, Object date1) throws ValidatorException {
Date joinedDate = (Date) date1;
UIInput retiredDateComponent = 
           (UIInput) component.getAttributes().get("retiredDateComponent");

String dateString = (String) retiredDateComponent.getSubmittedValue();
System.out.println("dateString>" + dateString);
        String pattern = "yyyy/MM/dd hh:mm";
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        Date retiredDate;
        try {
        retiredDate = dateFormat.parse(dateString);        
        } catch (ParseException e) {
            e.printStackTrace();
            return;
        }
        System.out.println("retiredDate> " + retiredDate);
if (joinedDate == null || retiredDate == null) {
            return;
}
        if (joinedDate.compareTo(retiredDate) > 0) {
        retiredDateComponent.setValid(false);
            throw new ValidatorException(new FacesMessage("Retired date should be 
                greator than joined Date"));
        }
    }
}
Now register your validator class in your 'faces-config.xml' file. To do it add the following lines directly within the <faces-config> tags.


<validator-id>dateComparator</validator-id>
  <validator-class>inova.DateComparator</validator-class>
</validator>
Note that 'dateComparator' is the name by using which you are going to access the validator in your page. You can give any name for this.

If you are using a date pattern in your second calendar(end date) component, you have to use the same date pattern in your validator class.
Your page will be as below.


<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:rich="http://richfaces.org/rich"
      xmlns:a4j="http://richfaces.org/a4j"
      xmlns:c="http://java.sun.com/jstl/core"
      xmlns:f="http://java.sun.com/jsf/core"> 


<head></head> 
<body>
  <h:form>
    Start date : 
    <rich:calendar id="cal_1">
      <f:validator validatorId="dateComparator"/>
      <f:attribute name="endDateComponent" value="#{endDate}"/>
    </rich:calendar>
    <br/>
    End date : <rich:calendar binding="#{endDate}" datePattern="yyyy/MM/dd hh:mm"/> 
    <br/>
    <rich:message for="cal_1" style="color:red;"/>
    <br/>
    <a4j:commandButton value="Save"/>
  </h:form>
</body> 
</html>

That is all. I think its clear for you what we have done.
♫ In our xhtml page we bind the 'end date calendar' component to the variable named as 'endDate' by using the 'binding' attribute of it.
♫ Then in our 'start date calendar' component, we use it as an attribute.
♫ In our validator class, we get that attribute by using the 'component.getAttributes().get(....)' method. Then we get the end date.
♫ If two dates are valid according to our criteria, we have nothing to do and if dates are invalid we throws a 'ValidatorException' with our own message.

2) Method 2(For seam users only)
This way is easier than the above method.
 The validator class is almost the same as above except three annotations are introduced before the class name as below.


@Name("dateComparator")
@org.jboss.seam.annotations.faces.Validator
@BypassInterceptors
public class DateComparator implements Validator {
..........
}
✱  Nothing to be put in faces-config.xml. It means you don't need to register your validator class in  faces-config.xml as in method1.

Enjoy......

Monday, July 9, 2012

Dynamically create tabs in <rich:tabPanel> component

In JSF you may have used <a4j:repeat> tag to repeat UI components. For an example you can use <a4j:repeat> tag as below to dynamically create several check boxes.

  <a4j:repeat value="#{userRoleList.resultList}" var="userRole">
    <h:selectBooleanCheckbox/>Click me<br/>
  </a4j:repeat>

The result will be as below.







But this doesn't work with <rich:tabPanel> and <rich:tab> components.
Is there any solution for this?
Yes, your old friend <c:forEach> component is still ready to help you.
Really you can use <c:forEach> of JSTL in your JSF page.
Following is an example. Required parts are colored.

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:rich="http://richfaces.org/rich"
      xmlns:c="http://java.sun.com/jstl/core"


<head></head> 
<body>
  <rich:tabPanel switchType="client">
    <c:forEach items="#{userRoleList.resultList}" var="userRole">
      <rich:tab label="Tab#{userRoleList.resultList.indexOf(userRole) + 1}">
        I am tab no. #{userRoleList.resultList.indexOf(userRole) + 1}
      </rich:tab>
    </c:forEach>
  </rich:tabPanel>
</body> 
</html>

Final result will be as below.

Tuesday, July 3, 2012

How to use <rich:listShuttle> component














In 'ListShuttle' component you have two lists.
The left hand side list is the Source list.Right hand side list is the Target list. When moving an item between two lists, it is passed as a String. So you have to write a converter class to convert this String again to the object. So in order to do this correctly you may need to override the 'toString' method of the class
which you are using as the list item.

In this example I am using following 'UserData' calss to represent an item in the list. So my source list and
target lists has the type of 'UserData'.
Note that 'toString' method in 'UserData' has been overridden.

public class UserData {
String userName;
Integer userId;
public String getUserName() {
  return userName;
  }
public void setUserName(String userName) {
  this.userName = userName;
  }
public Integer getUserId() {
  return userId;
  }
public void setUserId(Integer userId) {
  this.userId = userId;
  }
  @Override
  public String toString() {
    return this.userId + "," + this.userName;
  }
}


My converter class is as below. It can convert a 'UserData' object to a String and, a String representation of 'UserData' object back to a 'UserData' object.
Following three annotations are must
@Name("userDataConverter") - This indicates the converter Id()
@Converter - This registers this class as a converter
@BypassInterceptors - Disabling interceptors.


import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.faces.Converter;
import org.jboss.seam.annotations.intercept.BypassInterceptors;


@Name("userDataConverter")
@Converter
@BypassInterceptors
public class UserDataConverter implements javax.faces.convert.Converter {


  public Object getAsObject(FacesContext fContext, UIComponent uiComp, String value) {
UserData userData = new UserData();
String[] parts = value.split(",");
userData.setUserId(Integer.valueOf(parts[0]));
userData.setUserName(parts[1]);
return userData;
  }


  public String getAsString(FacesContext fContext, UIComponent uiComp, Object obj) {
UserData userData = (UserData) obj;
return userData.toString();
  }
}

My seam class which is bound to the page is as below.

import java.util.ArrayList;
import java.util.List;
import org.jboss.seam.annotations.Name;


@Name("myBean")
public class MyBean {
List<UserData> sourceData;
List<UserData> targetData = new ArrayList<UserData>();

public MyBean() {
sourceData = new ArrayList<UserData>();
UserData u = new UserData();
u.setUserId(100);
u.setUserName("John");
sourceData.add(u);
UserData u2 = new UserData();
u2.setUserId(101);
u2.setUserName("Kate");
sourceData.add(u2);
}

public List<UserData> getSourceData() {
return sourceData;
}

public List<UserData> getTargetData() {
return targetData;
}
}

Finally the ListShuttle component in my xhtml page is this.

    <rich:listShuttle sourceCaptionLabel="All users" targetCaptionLabel="Selected users"  orderControlsVisible="true"  sourceValue="#{myBean.sourceData}" var="userData" 
   targetValue="#{myBean.targetData}" converter="userDataConverter">
      <rich:column width="60px">
        <f:facet name="header">
          <h:outputText value="Username"/>
        </f:facet>
        <h:outputText style="cursor:pointer;" value="#{userData.userName}" />
      </rich:column>
      <rich:column width="60px">
        <f:facet name="header">
          <h:outputText value="Id" />
        </f:facet>
        <h:outputText style="cursor:pointer;" value="#{userData.userId}" />
      </rich:column>
    </rich:listShuttle>

Customize JSF selectOneRadio component

The general use of  '<h:selectOneRadio>' component is as below

    <h:selectOneRadio>
      <f:selectItem itemLabel=": Cheque" itemValue="1" />
      <f:selectItem itemLabel=":Cash" itemValue="2" />
    </h:selectOneRadio>

The appearance of finally rendered component is as below. The Select Items are laid horizontally by default.





If you want to arrange them vertically then change the 'layout' attribute as below.

    <h:selectOneRadio layout="pageDirection">
      <f:selectItem itemLabel="Cheque" itemValue="1" />
      <f:selectItem itemLabel="Cash" itemValue="2" />
    </h:selectOneRadio>






However if you want to place the labels of 'Select Items' components before the radio buttons you have to use css.


    <style>
      .myRad td {
          text-align:right;
      }
      .myRad td input {
          float:right;
          width:35px;
      }
    </style>


    <h:selectOneRadio layout="pageDirection" styleClass="myRad">
      <f:selectItem itemLabel="Cheque :" itemValue="1" />
      <f:selectItem itemLabel="Cash :" itemValue="2" />
    </h:selectOneRadio>

Final result will be as below.