JSF Custom Components: Tag Files, Composite Components & Custom Components

In JSF 2.x reusable components can be created using any one of the three approaches: Tag Files, Composite Components or Custom Components.

Templates and facelets are at the core of re-usability feature of JSF, so before we begin discussing custom components, here is a very brief intro about facelets and templates.

Facelets

In JavaServer Faces (JSF) 2.x, Facelets is the default view declaration language. Facelets is a light weight templating framework. A typical facelet application may consist of multiple facelets, some of which could be re-usable facelets, for example header and footer; while others like the main business facelet could be different in each facelet application. What's important is that all facelets in a template view work as an entity.

Template

A template file is basically an XHTML file which defines the overall structure or layout of a page. Different facelets application can refer to same template to render pages with a common layout.
As mentioned above that re-usability is built into the core of JSF framework, even if you don't use the advanced features (which we are going to talk about shortly), reusable code can be created in JSF using facelets and templates.

Tag files

Tag files is a way to define custom tag. This is generally used when there are multiple tags to be rendered but for brevity or to reduce code repetition, only one tag is used i.e. it is merely a way to combine two or more existing components/tags into one.

Lets look at an example: This example uses Apache "Tomahawk" JSF component libraries "dataTable" component. This component has an inbuilt column 'sort' feature and one can provide custom images to show sort order (ASC or DESC).

In "dataTable" component, "column header" and "sorting" information is defined in a header facet. Now as mostly is the case, a dataTable usually has more than one column; so having a header facet with sort image information in every column declaration clutters the code (see highlighted code).

<t:dataTable id="contactList" var="contact" value="#{contactListViewBean.contacts}" sortable="true" sortColumn="#{contactListViewBean.sort}" sortAscending="#{contactListViewBean.ascending}">
  <t:column sortable="true">
   <f:facet name="header">
    <t:commandSortHeader columnName="contactName" arrow="false" id="cNameId">
     <h:outputText value="ContactName" />
     <f:facet name="ascending">
      <t:graphicImage value="uparrow.png" rendered="true" alt="Soem desc" />
     </f:facet>
     <f:facet name="descending">
      <t:graphicImage value="downarrow.png" rendered="true" alt="Soem desc" />
     </f:facet>
    </t:commandSortHeader>
   </f:facet>
   <h:link id="contactNameId" value="#{contact.displayString}" outcome="ViewContact">
    <f:param name="contactId" value="#{contact.primaryKey}" />
   </h:link>
  </t:column>
  <t:column sortable="true">
   <f:facet name="header">
    <t:commandSortHeader columnName="contactName" arrow="false" id="cAddrId">
     <h:outputText value="ContactAddress" />
     <f:facet name="ascending">
      <t:graphicImage value="uparrow.png" rendered="true"alt="Soem desc" />
     </f:facet>
     <f:facet name="descending">
      <t:graphicImage value="downarrow.png" rendered="true" alt="Soem desc" />
     </f:facet>
    </t:commandSortHeader>
   </f:facet>
   <t:outputText id="contactAddressId" value="#{contact.contactAddress.displayString}" title="Address of the Contact" />
  </t:column>
</t:dataTable>

Now the goal is to reduce the code clutter because of code repetition using Tag file feature of JSF2.0.
Following is a list of to do or items that are required to implement custom tag using tag files.

Step No.Description
1Create a source(xhtml) file and define contents in it using ui:composition tag. This file is put in web-inf/tags folder of the web application.
2Create a tag library descriptor (.taglib.xml file) and declares the above custom tag in it. Make sure that the namespace and tag name is unique for the application. This file is put in WEB_INF folder of the web application.
3Register the tag libray descriptor in web.xml
4Use above declared tag in a XHTML file

Putting the above steps in perspective of our example- put all the common code of header declaration in a tag file (step 1), and then define a custom tag for simplified access to the common code (step 2), register the new tag (step 3) and then use the new tag in a XHTML page (step 4)

Step 1. Create a source file/facelet containing all the tags/components required for column header.  

DataTableColumn.xhtml


Create a folder "tags" in the webroot/WEB-INF folder of your project and save file as DataTableColumn.xhtml. If you notice the value of attributes "id" and "columnName" are EL variables -  "column_Name_Attr" & "header_Name_Attr"; as you will see in step 4. these are nothing but attributes of the custom tag and you can pass in values to customize the column header information.

Step 2. Create a custom tag - this is done by creating a library desciptor file "column.taglib.xml" containing unique namespace, tagname and location of the source file.
column.taglib.xml.
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibary_2_0.xsd" version="2.0">
    <namespace>http://incept.com/column</namespace>
    <tag>
        <tag-name>tableColumn</tag-name>
        <source>tags/DataTableColumn.xhtml</source>
    </tag>
</facelet-taglib>
The above code defines a custom tag "tableColumn" in namespace "http://incept.com/column"; we will see how it is used in step 4. Save this file in WEB-INF folder of the project.

Step 3. Register tag library in web.xml
web.xml
<context-param>
  <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
  <param-value>/WEB-INF/column.taglib.xml</param-value>
</context-param>

Step 4. Use newly defined/declared tag in a Facelet

At the top of the xhtml file, first define the namespace for tag
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" 
  xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" 
  xmlns:t="http://myfaces.apache.org/tomahawk"  
  xmlns:lsColumn="http://incept.com/column" lang="en">

and use the new tag as follows
<t:dataTable id="contactList" styleClass="scrollerTable" var="contact" value="#{contactListViewBean.contacts}" sortable="true" sortColumn="#{contactListViewBean.sort}" sortAscending="#{contactListViewBean.ascending}" rows="50" title="Results for selection" rowCountVar="tabIndexCount" rowIndexVar="row">
  <t:column sortable="true">
    <lsColumn:tableColumn column_Name_Attr="contactName" header_Id_Attr="contact_name_Header" header_Name_Attr="Name" />
    <h:link id="contactNameId" value="#{contact.displayString}" outcome="ViewContact">
      <f:param name="contactId" value="#{contact.primaryKey}" />
    </h:link>
  </t:column>
  <t:column sortable="true">
    <lsColumn:tableColumn column_Name_Attr="contactAddress" header_Id_Attr="contact_address_Header"  header_Name_Attr="Address" />
    <t:outputText id="contactAddressId" value="#{contact.contactAddress.displayString}" title="Address of the Contact" />
  </t:column>
</t:dataTable>
Custom defined tag "tableColumn" very nicely replaced all the clutter of header facet with just one line of code and at the same time header information of every column is customized by using attributes "column_Name_Attr" and "header_Id_Attr" and "header_Name_Attr".

Composite Component

This process requires less work as compared to previously discussed Tag Files. Basically we no longer need a taglib file and don't need to register it in web.xml either.

Step No.Description
1Create Composite Component: Create a source(xhtml) file and use composite:interace and composite:implementation ( where xmlns:composite="http://java.sun.com/jsf/composite") tags to define interface (attributes, methods) and implementation (Using other JSF tags to render etc.).
2Save the source file in resources/<subfolder> folder of the web application.
3Use above defined tag/composite component in a facelet.

Here is a very simple source file.

Step 1.  Create a source file: LabelAndField.xhtml



Things to note are
1)   Namespace declaration at the top of the file
                              xmlns:composite="http://java.sun.com/jsf/composite" 
2)  Interface declaration within  <composite:interface>: Contents within this tags define the usage contract for a composite component. This section defines configurable items (attributes, actions etc) of the composite tag. This tag also has an optional attribute "name", which if declared becomes the name of the tag else by default the tagName is same as file name.
3)  Implementation declaration within  <composite:implementation>; this section contains the actual work or the XHTML markup of the composite tag. The attributes and methods declared within the <composite:interface>block can be accessed within <composite:implementation> by using EL syntax #{cc.attrs.atribute-Name}.
The word "cc" is reserved in JSF for composite components and can give you things like the parent component, children in a list, ID etc., "attrs" refers to attributes, cc.attrs gives you a map of attributes defined in the "interface" section and you can access a specific attribute using the "attribute-Name".

Step 2.  Save the file in web-inf/resources/<subfolder>


In the above example the file is stored in resources/ezcomp. Step 3 points out significance of folder/path..

Step 3. Use composite tag in a facelet. Create a facelet, say - compositedemo.xhtml



Things to note are
1)   Namespace declaration at the top of the file
                              xmlns:ezcomp="http://java.sun.com/jsf/composite/ezcomp" 
      The first part defines that the namespace for the composite component is "ezcomp", and the second part declares where in the resources folder to find the definition of this composite component. In this example its in the "ezcomp" subfolder inside resources.
2)  This is how you use the tag: namespace:FileName; inour example ezcomp:LabelAndField, LabelAndField is the name of the file, so by default its the tag name;
<ezcomp:LabelAndField opLabelId="fNLabel" opLabelName="First Name"
                                  fieldId="fnInput" bindTo="#{person.firstName}"
/>

Run/ deploy file and you should see



Custom Components


Custom components are required if the existing components provided by the default implementation (mojarra or myfaces) or the extended components (primefaces etc) do not provide the desired functionality.
In cases, where in you need to make addition/change to markup generated by JSF components, then you can use a custom renderer.
Here is an example of how to add ARIA specific attribute to standard components. (By default, if you include an unspecified attribute in an jsf component, that attribute is dropped ie. not rendered in the resultant html)

Following is a list of to do or items that are required to implement custom renderer.

Step No.Description
1Create a custom renderer extending the renderer type.
2Register the renderer in faces-config.xml.
3Use standard tag in a XHTML file

Step. 1. Create Custom Renderer
This example extends standard "TextRenderer" to render ARIA attributes in the rendered html.
public class MyInputTextRenderer extends TextRenderer {

  @Override
  protected void getEndTextToRender(FacesContext context, UIComponent component, String currentValue)
  throws java.io.IOException {
    String[] attributes = {"aria-required", "aria-readonly", "aria-autocomplete","aria-multiline"};
    ResponseWriter writer = context.getResponseWriter();
    for (String attribute : attributes) {
      String value = (String) component.getAttributes().get(attribute);
      if (value != null) {
        writer.writeAttribute(attribute, value, attribute);
      }
    }
    super.getEndTextToRender(context, component, currentValue);
  }
}
The above example extends renderer for text fields -TextRenderer. If the input filed contains any of the ARIA attributes defined in the "attributes" array, then that attribute and its value is rendered in the resultant html as well.

Step. 2. Register renderer in faces-config.xml
<render-kit>
  <renderer>
    <component-family>javax.faces.Input</component-family>
    <renderer-type>javax.faces.Text</renderer-type>
    <renderer-class>com.incept.MyInputTextRenderer</renderer-class>
  </renderer>
</render-kit>

Step. 3. Use standard tag in XHTML file
<h:outputLabel id="labelFortxt1" for="txt1" value="Aria Field"/>
<h:inputText id="txt1" value="Hello" aria-required="true" aria-readonly="true" 
                   aria-Not_A_Declared_Attr="ShouldNotDisplay"/>

The resultant/rendered html will contain "aria-required" and "aria-readonly" attributes. Attribute "aria-Not_A_Declared_Attr" is not displayed because its not in the "attributes" array defined in the renderer.


Prasanna Bhale

6 comments: