Problem Statement

Let’s say that we want to trigger an ajax request when the user changes the value in a <rich:calendar />, but it has to work when the user types the date into the text field manually, as well as when the user uses the calendar to pick the date.

<rich:calendar /> defines two events just for this purpose:

  • “change”: Triggered when the date is changed from the calendar
  • “inputchange”: Triggered when the text in the text field is changed manually

But how do we combine them?

Solution

I already had my calendar inside a Composite JSF Component, so my solution uses the following approach.

YourWebApp/WebContent/resources/components/calendar.xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

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

<composite:interface>
	<composite:attribute name="value" required="true" />
	<composite:attribute name="timeZone" required="true" />
	<composite:attribute name="disabled" default="false" />
	<composite:attribute name="displayValueOnly" default="false" />
	<composite:attribute name="required" />

	<composite:clientBehavior name="date_change" event="change" targets="#{cc.id}"/>
	<composite:clientBehavior name="date_change" event="inputchange" targets="#{cc.id}"/>
</composite:interface>

<composite:implementation>

	<c:if test="#{cc.attrs.displayValueOnly == false}">
		<rich:calendar id="#{cc.id}" 
				inputSize="10" 
				required="#{cc.attrs.required}" 
				enableManualInput="true" 
				value="#{cc.attrs.value}" 
				disabled="#{cc.attrs.disabled}" 
				datePattern="yyyy-MM-dd"
				onchange="if (DirtyFieldHandler) DirtyFieldHandler.setDirty();">
			<f:convertDateTime pattern="yyyy-MM-dd" timeZone="#{cc.attrs.timeZone}" />
		</rich:calendar>
	</c:if>

	<c:if test="#{cc.attrs.displayValueOnly == true}">
		<h:inputText value="#{cc.attrs.value}" disabled="true" id="#{cc.id}InputDate" size="10">
			<f:convertDateTime pattern="yyyy-MM-dd" timeZone="#{cc.attrs.timeZone}" />
		</h:inputText>
	</c:if>

</composite:implementation>
</html>

Then we use the component like so:

  <comp:calendar value="#{backingBean.approvedDate}">
     <a4j:ajax event="date_change" execute="@this" />
  </comp:calendar>