Creating custom components
Creating custom components overview
JavaServer Faces technology offers a rich set of standard, reusable UI components that we can use to construct UIs for Web applications easily. Additionally, the flexible architecture of JSF allows us to create custom components.
In our sample application, we created a custom component to display e-mail addresses. The e-mail address output component enables the OS to invoke an e-mail client automatically when the user clicks the e-mail address. Also, this component will hide the e-mail address from Web bots, putting a halt to spam in the early stages.
Creating the component tag handler
Creating a component tag is very similar to creating a custom JSP tag. javax.faces.webapp.UIComponentTag is the base class for all JSF component tag handlers. Here is the tag handler class for our e-mail address output component:
/*
* $Id: UIOutputEmailTag.java,v 1.1 2003/06/20 16:48:49 jack Exp $
*
*/
package net.jackwind.jsf;
import javax.faces.component.UIComponent;
import javax.faces.webapp.UIComponentTag;
/**
* @author JACK (Jun 19, 2003)
* @class UIOutputEmail
*/
public class UIOutputEmailTag extends UIComponentTag {
private String valueRef;
private String value;
protected String getComponentType() {
return "UIOutputEmail";
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String getValueRef() {
return valueRef;
}
public void setValueRef(String newValueRef) {
valueRef = newValueRef;
}
public String getRendererType() {
return null;
}
protected void overrideProperties(UIComponent component) {
super.overrideProperties(component);
UIOutputEmail email = (UIOutputEmail) component;
if (value != null) {
email.setValue(value);
}
if(email.getValueRef() == null && valueRef != null) {
email.setValueRef(valueRef);
}
}
}
|
There are two abstract methods in UIComponentTag:
getComponentType() and getRendererType().
The getComponentType() method returns the component type for the component that is bound to the tag. The other method, getRendererType(),
selects the Renderer to be used for rendering the component. In the code above, getRendererType() returns null,
which means that the component manages rendering itself without using external renderers. overrideProperties() is used to override properties of the component if the corresponding properties of this tag handler were explicitly set, and the corresponding attribute of the component has not been not set. The rest are typical JavaBeans methods that can be used to get and set element values.
Creating the tag library descriptor
The tag library descriptor (TLD) associated with the output_email tag is illustrated below:
<?xml version="1.0" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>JSF Email Component by Jack Li Guojie.</short-name>
<display-name>JSF Email Component by Jack Li Guojie.</display-name>
<description>
This library contains custom email component for JSF
</description>
<tag>
<name>output_email</name>
<tag-class>net.jackwind.jsf.UIOutputEmailTag</tag-class>
<body-content>JSP</body-content>
<description>JSF Email Component by Jack Li Guojie.
</description>
<attribute>
<name>id</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>value</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>valueRef</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
|
This TLD is defined in the file WEB-INF/email-tag.tld.
There are three attributes: id is used to set the ID for this component; the other two attributes are used to set the value -- the e-mail address. If the e-mail address is stored in a bean, then valueRef should point to the property of the bean. The value attribute can be set to a static e-mail address directly, like so:
<jack:output_email id="outemail" value="jack@jackwind.net" /> |
A component class defines the state and behavior of a UI component. The state information includes the component's type, identifier, and local value. Some examples of behavior defined by component class are decoding (converting the request parameter and other information to the component's local value), encoding (converting a local value to some markup), or updating model objects.
We create the e-mail address output component by extending the javax.faces.component.UIOutput class.
/*
* $Id: UIOutputEmail.java,v 1.1 2003/06/20 16:48:49 jack Exp $
*
*/
package net.jackwind.jsf;
import java.io.IOException;
import java.util.StringTokenizer;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
/**
* @author JACK (Jun 19, 2003)
* @class UIOutputEmail
*/
public class UIOutputEmail extends UIOutput {
private String host;
private String user;
private void parseEmail(String email) {
StringTokenizer st = new StringTokenizer(email, "@");
if (st.countTokens() != 2)
throw new IllegalArgumentException(
"Invalid email address: " + email);
user = st.nextToken();
host = st.nextToken();
}
// This method indicates whether this component renders itself
// or delegates rendering to a renderer.
public boolean getRendersSelf() {
return true;
}
// Called during the Render Response phase
public void encodeEnd(FacesContext context) throws IOException {
if(user == null || host == null) {
try {
parseEmail((String)currentValue(context));
}catch(Exception e) {
LogUtil.log(e);
}
}
ResponseWriter writer = context.getResponseWriter();
// Represent this component as HTML with JavaScripts
writer.write("<script language='JavaScript'>\n");
writer.write("function emailAddress(host, user) { \n");
writer.write("document.write('<a href=\"mailto:');\n");
writer.write("document.write(user + '@' + host);\n");
writer.write("document.write('\">');\n");
writer.write("document.write(user + '@' + host);\n");
writer.write("document.write(\"</a>\");\n}\n\n");
writer.write("emailAddress('" + host + "', '" +
user + "');\n");
writer.write("</script>\n");
}
protected String getComponentType() {
return "output_email";
}
}
|
Note that we override the encodeEnd() method to render the component into HTML markup. This method will be called during the Render Response phase of the JSF life cycle.
To register the component, we need to put a component entry into the application configuration file. Here is the code to register the e-mail address output component:
<!-- ============== Custom components ========================= --> <component> <component-type>UIOutputEmail</component-type> <component-class>net.jackwind.jsf.UIOutputEmail</component-class> </component> |
We've now created and registered our custom component. It can be used in the same way as standard components. The code below displays an e-mail address that is stored in the bean (see the file thanks.jsp):
<%@ taglib uri="/WEB-INF/jsf-demo-jack.tld" prefix="jack" %> ... <jack:output_email id="outemail" valueRef="subscriberBean.email" /> |
And here's the HTML output from this component:
<script language='JavaScript'>
function emailAddress(host, user) {
document.write('<a href="mailto:');
document.write(user + '@' + host);
document.write('">');
document.write(user + '@' + host);
document.write("</a>");
}
emailAddress('jackwind.net', 'jack');
</script>
|

