Sunday, 27 November 2011

JSP: arbitrary attributes on JSF controls

One criticism developers have of JavaServer Faces is that it is not possible to add arbitrary attributes to the resultant markup. Problems arise when they wish to add custom attributes specific to JavaScript frameworks (e.g. dojoType for the Dojo toolkit) or HTML 5 attributes (such as data- or placeholder.)

However, this is not a technical limitation of the JSF framework; only a limitation of the standard control library.

All discussion and code that follows relates to J2EE 1.4 (Servlet 2.4; JSP 2.0; JSF 1.1.) I'm currently working on a legacy platform.

The attribute tag in JSF

You can already add arbitrary JSF attributes to components using the attribute tag. These end up on the attributes map. However, these will be ignored unless you implement a custom renderer to take advantage of them. They must work round existing property names on the component and result in overly verbose markup:

<x:foo>
  <f:attribute name="style" value="text: bold;">
  <f:attribute name="data-bar-baz" value="#{xxx}">
</x:foo>

JSP tag libraries

Custom JSP tags are generally defined via tag library descriptors (TLDs.) Tag handlers usually provide a fixed list of attributes with strong type information. However, JSP 2.0 introduced the DynamicAttributes interface; any tag handler that implements it and declares <dynamic-attributes>true</dynamic-attributes> can handle attributes that aren't explicitly defined.

JSF 1.1 controls that take arbitrary attributes can be created by extending UIComponentTag and implementing DynamicAttributes.

Note that prior to the Unified Expression Language deferred expressions of the form #{foo} would always be strings and the JSF tag was responsible for their evaluation. In JSP 2.1 onwards the container can intervene and convert these to ValueExpressions. Containers from different vendors are not consistent in what objects they will pass if a deferred expression is included in the value of a dynamic attribute.

Applying this idea

<g:input id="dob" name="dob" jsf-tag="input"
  jsf-valueChangeListener="#{testValue.change}"
  jsf-binding="#{components.in}" jsf-in="#{param['dob']}"
  type="text" placeholder="Type something" jsf-value="#{testValue.foo}"
  value="#{testValue.foo}" />

In the above control, all attributes are dynamic. The tag handler will consider anything prefixed with jsf- to be a JSF property to be applied to the control. Anything else will be rendered as a pass-through attribute. The jsf-in attribute is evaluated to get the submitted value. Here is the resulant markup:

<input id="dob" placeholder="Type something"
    name="dob" value="" type="text"></input>

The control not only lets us emit any attribute, it allows more control over the client identifier.

Sample code

A small library implementing this idea is available in a public Subversion repository. Further documentation can be found in the gurn/doc/gurn.html file.

Repository: http://illegalargumentexception.googlecode.com/svn/trunk/code/java/
License: MIT
Library Project: gurn
Test WAR Project: gurn-tests11

No comments:

Post a Comment

All comments are moderated