Writing Custom Facelet JSF Components

Writing Facelet JSF Components is NOT the same thing as writing custom JSP-based JSF Components. Once you get past that it turns out they’re not hard to write at all. There’s no mucking around with JSP tag libraries, TLD files and other such archaic nonsense.

You need to edit create/edit three files in total. Here’s an example of a Facelet JSF component that prints out a table row when it is nested around another UI component. For example, the component if used the following way:

<arse:tableRow>
    <h:inputText id="myId" label="My Label"/>
</arse:tableRow>

would print

<tr>
  <td>My Label</td>
  <td><input type="text" ..../></td>
</tr>

Simple, right? Here’s the three files you have to create/edit:

faces-config.xml

Make sure the following is present in your faces-config.xml:

    
    <component>
        <component-type>tableRow</component-type>
        <component-class>com.arsenalist.UITableRow</component-class>
    </component>

META-INF/mytag-taglib.xml

Create a file withe following contents under your META-INF folder:

<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
  "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
  "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">

<facelet-taglib>
    <namespace>http://www.arsenalist.com/jsf/util</namespace>
    <tag>
        <tag-name>tableRow</tag-name>
        <component>
            <component-type>tableRow</component-type>
            <renderer-type>tableRow.renderer</renderer-type>
        </component>
    </tag>
</facelet-taglib>

Note the namespace, that’s what you’ll need to refer in the Facelet before using the tag, e.g:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:arse="http://www.arsenalist.com/jsf/util"
      . . . 
&#91;/sourcecode&#93;

<h2><code>UITableRow.java</code></h2>

Finally, create the actual component.  Here's an example of a component that wraps the child elements around a tr and a couple tds.  We must override <code>getRendersChildren()</code> to return true because it is the component's duty to render its children (since we're wrapping it around stuff).


package com.arsenalist;


import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import java.io.IOException;

/**
 * This class renders a table row.  It must contain two controls, the label and control to render.
 * @author Zarar Siddiqi
 */
public class UITableRow extends UIOutput {

    public UITableRow() {
        setRendererType(null);
    }

    public boolean getRendersChildren() {
        return true;
    }

    public void encodeBegin(FacesContext facesContext) throws IOException {
        // We're rendering a table row
        facesContext.getResponseWriter().startElement("tr", null);
    }

    @Override
    public void encodeChildren(FacesContext facesContext) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();

        UIComponent control = getChildren().get(0);

        writer.startElement("td", label);
        // Label goes inside td
        writer.startElement("label", label);
        writer.writeAttribute("for", control.getClientId(facesContext), null);

        writer.write((String) control.getAttributes().get("label"));
        writer.endElement("label");
        writer.endElement("td");

        // Div element goes in the second along with the control
        writer.startElement("td", control);
        control.encodeEnd(facesContext);
        writer.endElement("td");
    }


    @Override
    public void encodeEnd(FacesContext facesContext) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        writer.endElement("tr");
    }

    @Override
    public String getFamily() {
        return COMPONENT_TYPE;
    }
}

And that’s that.

Advertisements

10 thoughts on “Writing Custom Facelet JSF Components

  1. Erik

    Hi!

    Nice tutorial. How would you go about implementing a custom renderer for a standard jsf html tag with Facelets?

    Or, to be precise, is there any way of substituting a custom renderer which doesn’t render a bunch of HTML table tags instead of the standard one, for all instances of h:selectOneRadio, while still retaining all of that tag’s functionality in terms of bean binding, etc.

    The documentation on this type of stuff is certainly pretty thin…

    Thanks.

    Reply
    1. Tom

      Erik,
      You can specify a custom renderer for a standard jsf tag. I wrote one for the richfaces inplaceSelect.
      N.B. I did this for facelets. JSP may need a bit more.
      I created a custom taglib.xml file. In there, I define a new tag item. In this item, I specify a component tag. In the component tag, define the component-type tag to match the JSF comonent type you are overriding. In my case, this was: org.richfaces.InplaceSelect. Then, add a renderer-type tag and give it a new type ( com.myDomain.InplaceSelectRenderer ).

      Then, in your faces-config.xml, define a render-kit with a new renderer tag. 3 tag are needed for the renderer: component-family, renderer-type, and renderer-class. The component-family needs to match the family of the JSF component you are overriding. The renderer-type must match the type specified in your custom tag definition. The class is the FQN of your renderer class.
      I fumbled my way through this, trying different combos. This way worked for me. Hope it helps!

      Reply
      1. Tom

        Sorry to have to add to my own post, but a clarification may be necessary. I keep saying “of the component you are overriding.” This is misleading. You are overriding the combination of JSF component and renderer. You are only writing/extending the renderer class.
        Tom

  2. Gorlok

    Good and nice post.

    I have a problem. Perhaps you can help me.

    Your component is self-rendered, but I need to use another class for render.

    It seens like tableRow.renderer has no effect in your example.

    Can you explain me how to do a not self-rendered component with Facelets? I have tried without any success until now 😦

    Thanks in advance.

    Reply
  3. Shantanu

    UITableRow.java will not compile. Make the following changes :
    1. UIComponent control = getChildren().get(0);
    change to
    UIComponent control = (UIComponent)getChildren().get(0);

    2. Change writer.startElement(“td”, label); to
    writer.startElement(“td”, this);

    Thanks for this tutorial. You first line says it all ‘Writing Facelet JSF Components is NOT the same thing as writing …’

    I’ve been trying to clarify this in my head for 2 days.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s