Friday, October 7, 2011

RichFaces 4 CDK | jQuery UI Calendar

Further incrementing the complexity over the input component we created previously, this time we will create a JSF calendar component. Being pragmatic OSS developers, we will leverage the existing javascript of the datepicker component from the jQuery UI project, and we'll see how well the RichFaces CDK lends itself to mapping JSF component attributes into javascript configuration options.

As always, if you are interested in following along in your IDE, you can get the code below on github.

The Component Class
Starting again with the component class:

AbstractDatepicker.java
package ca.bleathem.richfaces.jquery.component;

import org.richfaces.cdk.annotations.*;

@JsfComponent(
        type = "ca.bleathem.richfaces.jquery.Datepicker",
        family = "ca.bleathem.Datepicker",
        renderer = @JsfRenderer(type = "ca.bleathem.jquery.DatepickerRenderer"),
        tag = @Tag(name="datepicker"))
abstract public class AbstractDatepicker extends javax.faces.component.UIInput {

    @Attribute
    public abstract String getDateFormat();

    @Attribute
    public abstract String getShowOn();

    @Attribute
    public abstract String getButtonImageOnly();

}

Here we see we are again extending the UIInput class, as we did with the Input component.  What's new is the introduction of some additional attributes.  The @Attribute annotation instructs the CDK that these abstract getter methods map to component attributes, and the CDK then takes care of wiring the attributes into the JSF state saving mechanism for us.

The naming convention of the attribute is alligned with jQeury UI datepicker options. This allows us to transparently pass the JSF component attributes through to the jQuery plugin, as we see in the Renderer below.

The Renderer
The renderer is again an xml file:

datepicker.template.xml
<?xml version="1.0" encoding="UTF-8"?>

<cdk:root xmlns="http://jboss.org/schema/richfaces/cdk/xhtml-el" xmlns:cdk="http://jboss.org/schema/richfaces/cdk/core"
    xmlns:c="http://jboss.org/schema/richfaces/cdk/jstl/core" xmlns:cc="http://jboss.org/schema/richfaces/cdk/jsf/composite"
    xmlns:javaee="http://java.sun.com/xml/ns/javaee">

    <cc:interface>
        <cdk:class>ca.bleathem.richfaces.jquery.renderkit.DatepickerRenderer</cdk:class>
        <cdk:superclass>ca.bleathem.richfaces.jquery.renderkit.DatepickerRendererBase</cdk:superclass>
        <cdk:renderer-type>ca.bleathem.jquery.DatepickerRenderer</cdk:renderer-type>
    </cc:interface>

    <cc:implementation>
        <input type="text" id="#{clientId}" name="#{clientId}" class="rf_jq_cal" value="#{getInputValue(facesContext, component)}" />
        <script type="text/javascript">
            <cdk:scriptObject name="pluginOptions">
                <cdk:scriptOption name="buttonImage" value="#{getResourcePath(facesContext, 'ca.bleathem', 'calendar.gif')}" />
                <cdk:scriptOption attributes="showOn dateFormat buttonImageOnly" />
            </cdk:scriptObject>
            jQuery(function() {
                $(document.getElementById('#{clientId}')).datepicker(#{toScriptArgs(pluginOptions)});
            });
	    </script>
    </cc:implementation>

</cdk:root>

Diving first into the cc:implementation of this renderer template, we see again an input element. What's new is the script tag following the input. This script tag get's compiled into javascript when the component renders. Using CDK xml markup, we build up the javascript object pluginOptions which we pass as a parameter to the call to the jQueryUI plugin.

The document.getElementById('#{clientId}') is a workaround for JSF's use of the ":" character in javascript IDs - this doesn't play well with jQuery. By first calling document.getElementById, we end up with an argument that is acceptible to jQuery. Notice the #{toScriptArgs(pluginOptions)} argument to the datepicker jQuery UI plugin. This initializes the datepicker plugin, with the JSF component attributes specified by the application developer.

From the cc:interface of this renderer template, we see that we are extending the class DatepickerRendererBase. This class is included below:

DatepickerRendererBase.java
package ca.bleathem.richfaces.jquery.renderkit;

import org.richfaces.renderkit.InputRendererBase;

import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;

@ResourceDependencies({
        @ResourceDependency(library = "javax.faces", name = "jsf.js"),
        @ResourceDependency(name = "jquery.js"),
        @ResourceDependency(library = "com.jqueryui/css/ui-lightness", name = "jquery-ui-1.8.16.custom.css"),
        @ResourceDependency(library = "com.jqueryui/development-bundle/ui", name = "jquery.ui.core.js"),
        @ResourceDependency(library = "com.jqueryui/development-bundle/ui", name = "jquery.ui.widget.js"),
        @ResourceDependency(library = "com.jqueryui/development-bundle/ui", name = "jquery.ui.datepicker.js"),
        @ResourceDependency(library = "ca.bleathem", name = "calendar.gif")
})
public class DatepickerRendererBase extends InputRendererBase {
}

The DatepickerRendererBase class is again extending the InputRendererBase, and it is holding a number of @ResourceDependency annotations. These annotations ensure the appropriate resources are included on the page when we reference this JSF component. For us, these resources are the jQuery UI plugins necessary to get the datepicker component working.

Introducing an entire new class merely to hold the @ResourceDependency annotations may seem like overkill, but in any reasonably complex component, we will end up putting some renderer helper logic into this class file. There are times when the expressivity of Java works better than the declarativity of xml. We'll see some of this later in our blog series.

Finally, let's look at en example use of this component:

sample.xhtml
<!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://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:b="http://bleathem.ca/test"
      >

<body>
<ui:composition template="/templates/template-jqueryui.xhtml">

    <ui:define name="title">Datepicker Sample</ui:define>

    <ui:define name="body">
        <h:form>
            <b:datepicker value="#{myBean.value}" dateFormat="yy-mm-dd" showOn="both" buttonImageOnly="true" /> <br />
            Input value: <h:outputText value="#{myBean.value}" /> <br />
            <h:commandButton value="Submit" action="null"/>
        </h:form>
    </ui:define>
</ui:composition>
</body>
</html>

When rendered, this looks like:

Screen shot

While this has certainly grown in complexity over our initial HelloWorld component, we've managed to keep our JSF component development DRY. We've leveraged existing, high-quality javascript components. We've also made use of the RichFaces CDK to wire the JSF configured component to the javascript object. Some additional work is required to continue mapping the jQuery UI datepicker plugin options into JSF component attributes, but the mechanism for this has been laid out, and completing the component will be quite straight forward (pull requests welcome!).

In my next CDK installment, we'll look at creating a layout based component, rather than an input component. Stay tuned!