Custom JSF Component 1.2
Since I had a hard time finding a complete solution anywhere online I decided that I would post a simple how-to for a JSF Custom component.
The problem that this component seeks to address is that the <f:verbatim> tag does not work properly in a <ui:repeat> tag.
This presented a issue to me because I have RAW HTML stored in my data model to be used in my simple blog application.
1. The most confusing thing about JSF Custom components. YOU NEED 3 SEPERATE CLASSES TO IMPLEMENT A CUSTOM COMPONENT.
Alot of other tutorials will teach you there is a method in which you can use only two classes to accomplish the task, although this is true the most straight forward approach is to use 3 classes to implement your component.
The are as follows:
- The component class (For this tutorial: HTMLVerbatimComponent) This class is technically where the “DATA” resisdes
- The tag class (For this tutorial: HTMLVerbatimTag) This class is the ADAPTER piece between the “JSF Page” and the DATA Model “Your component”
- The renderer class (For this tutorial: HTMLVerbatimRenderer) This class is response for the actual rendering of the HTML or OUTPUT.
The component class:
package com.mdb.web.jsf.component; import javax.faces.component.UIComponentBase; import javax.faces.context.FacesContext; import javax.el.ValueExpression; public class HTMLVerbatimComponent extends UIComponentBase { public HTMLVerbatimComponent() { } //Properties you want exposed as tag values must be exposed using getters/setters private String value; public String getValue() { if(value != null) return value; //If the value has not already been set interpret the Expression language and get resulting object Object value = this.getValueExpressionValue("value"); return value != null ? (String)value : null; } public void setValue(String value) { this.value = value;} //Honestly I am not sure what this method does besides link the component to the config files public String getFamily() { return "com.mdb.web.jsf.component.HTMLVerbatim"; } //Method allows component state to be stored @Override public Object saveState(FacesContext context) { return new Object[] { super.saveState(context), value}; } //Method allows component state to be restored @Override public void restoreState(FacesContext context, Object object) { Object state[] = (Object[]) object; super.restoreState(context, state[0]); value = (String)state[1]; } //Helper method to simplify getters/setters //This helps the JSF Component use Expression Language private Object getValueExpressionValue(String name) { ValueExpression ve = super.getValueExpression(name); return ve.getValue(FacesContext.getCurrentInstance().getELContext()); } }
The tag class:
package com.mdb.web.jsf.component; import javax.faces.webapp.UIComponentELTag; import javax.faces.component.UIComponent; import javax.el.ValueExpression; public class HTMLVerbatimTag extends UIComponentELTag { //Temporary holder property hence if I put //<tag:htmlVerbatim value="#{SomeBackingBean.htmlText}"/> //information is stored in this object until the data is pushed into the component in setProperties method private ValueExpression value = null; public HTMLVerbatimTag() {} public ValueExpression getValue() { return this.value;} public void setValue(ValueExpression value) { this.value = value;} //This is where the action happens //Data is taken from the tag on the JSF Page and placed into the Component @Override protected void setProperties(UIComponent component) { super.setProperties(component); HTMLVerbatimComponent verbatimComponent = (HTMLVerbatimComponent)component; if(value != null) { verbatimComponent.setValueExpression("value", value); } } //Method links this tag object to the component in config files public String getComponentType() { return "com.mdb.web.jsf.component.HTMLVerbatim"; } //Method links this tag object to the renderer in config files public String getRendererType() { return "com.mdb.web.jsf.component.HTMLVerbatim";} }
The renderer class:
package com.mdb.web.jsf.component;import javax.faces.render.Renderer; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import java.io.IOException; public class HTMLVerbatimRenderer extends Renderer {public HTMLVerbatimRenderer() {} //Render your HTML HERE @Override public void encodeBegin(FacesContext context, UIComponent component) throws IOException { HTMLVerbatimComponent verbatimComponent = (HTMLVerbatimComponent)component; ResponseWriter writer = context.getResponseWriter(); writer.write(verbatimComponent.getValue()); writer.flush(); } }
Now you must make some changes to your configuration files and I make the big assumption you are using Facelets.
Create a file in your WEB-INF directory called: custom.taglib.xml
Add the following content (Edit accordingly for your own component and domain):
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd"> <facelet-taglib> <namespace>http://mechdrives.com/web/jsf/component/tags</namespace> <tag> <tag-name>htmlVerbatim</tag-name> <component> <component-type>com.mdb.web.jsf.component.HTMLVerbatim</component-type> <renderer-type>com.mdb.web.jsf.component.HTMLVerbatim</renderer-type> </component> </tag> </facelet-taglib>
Edit your faces-config.xml and add the following content(Edit accordingly for your own component and domain):
<faces-config version="1.2"> 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-facesconfig_1_2.xsd"> <component> <component-type>com.mdb.web.jsf.component.HTMLVerbatim</component-type> <component-class>com.mdb.web.jsf.component.HTMLVerbatimComponent</component-class> </component> <render-kit> <renderer> <component-family>com.mdb.web.jsf.component.HTMLVerbatim</component-family> <renderer-type>com.mdb.web.jsf.component.HTMLVerbatim</renderer-type> <renderer-class>com.mdb.web.jsf.component.HTMLVerbatimRenderer</renderer-class> </renderer> </render-kit> <application> <locale-config> <default-locale>en</default-locale> </locale-config> <view-handler>com.sun.facelets.FaceletViewHandler</view-handler> <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> </application> </faces-config>
Edit your web.xml and add the following content:
<context-param> <param-name>facelets.LIBRARIES</param-name> <param-value>/WEB-INF/custom.taglib.xml;</param-value> </context-param>
Now you should be able to use your new component/tag simply by adding a namespace to your JSF Page as such:
then using the tag as such:
<custom:htmlverbatim value="#{SomeBackingBean.htmlText}"> </custom:htmlverbatim>