Using tag handler, UI component and renderer with a JSF facelet taglib
November 22, 2011 10:40:16 Last update: November 22, 2011 10:40:16
This is an example that uses tag handler, UI component and renderer together to support a custom taglib. The main purpose is to show how these components play together.
The tag renders
as
These are the files:
The tag renders
<ui:param name="extra" value="el interpreted"/> <h3>my:foreach</h3> <my:foreach var="person" value="John Joe Jane" tag="ul" class="css class" extra="#{extra}"> <li>#{person}</li> </my:foreach>
as
<h3>my:foreach</h3> <ul class="css class" extra="el interpreted"> <li>John</li> <li>Joe</li> <li>Jane</li> </ul>
These are the files:
- The tag handler (
src/main/java/com/example/ForeachTagHandler.java):package com.example; import java.util.Map; import javax.faces.component.UIComponent; import javax.faces.view.facelets.*; public class ForeachTagHandler extends ComponentHandler { public ForeachTagHandler(ComponentConfig config) { super(config); } @Override protected MetaRuleset createMetaRuleset(Class cls) { MetaRuleset meta = super.createMetaRuleset(cls); meta.alias("class", "styleClass"); return meta; } @Override public void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) { TagAttribute[] allAttrs = this.tag.getAttributes().getAll(); // we are passing thru all attributes except tag, var and value. String[] attrs = new String[allAttrs.length - 3]; int j = 0; for (int i = 0; i < allAttrs.length; i++) { String localName = allAttrs[i].getLocalName(); if ("tag".equals(localName) || "var".equals(localName) || "value".equals(localName)) { continue; } if ("class".equals(localName)) { attrs[j++] = "styleClass"; } else { attrs[j++] = localName; } } Map<String,Object> compAttrs = c.getAttributes(); compAttrs.put("alias.attributes", attrs); } }
- The UI component (
src/main/java/com/example/UIForeach.java):package com.example; import java.io.IOException; import javax.el.ValueExpression; import javax.faces.component.UIComponent; import javax.faces.component.UIComponentBase; public class UIForeach extends UIComponentBase { public static final String COMPONENT_TYPE = "com.example.Foreach"; public static final String COMPONENT_FAMILY = "com.example"; // data object private Object value; // variable name private String var; private String tag; public UIForeach() { } public String getFamily() { return COMPONENT_FAMILY; } public String getVar() { return this.var; } public void setVar(String var) { this.var = var; } public String getTag() { return this.tag; } public void setTag(String tag) { this.tag = tag; } // value is set only when the value attribute does not contain EL. // otherwise, setValueExpression is called with the EL value. public Object getValue() { if (this.value == null) { ValueExpression vex = this.getValueExpression("value"); if (vex != null) { return vex.getValue(getFacesContext().getELContext()); } } return this.value; } public void setValue(Object value) { this.value = value; } public boolean getRendersChildren() { return true; } }
- The renderer (
src/main/java/com/example/ForeachRenderer.java):package com.example; import java.io.IOException; import java.util.Map; import java.util.Iterator; import javax.faces.render.Renderer; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; public class ForeachRenderer extends Renderer { @Override public void encodeBegin(FacesContext ctx, UIComponent uic) throws IOException { UIForeach c = (UIForeach) uic; ResponseWriter w = ctx.getResponseWriter(); w.startElement(c.getTag(), c); Map<String, Object> a = c.getAttributes(); String[] attrs = (String[]) a.get("alias.attributes"); for (int i = 0; i < attrs.length; i++) { String attr = attrs[i]; if ("styleClass".equals(attr)) { w.writeAttribute("class", a.get(attr), null); } else { w.writeAttribute(attr, a.get(attr), null); } } } @Override public void encodeEnd(FacesContext ctx, UIComponent uic) throws IOException { UIForeach c = (UIForeach) uic; ResponseWriter w = ctx.getResponseWriter(); w.endElement(c.getTag()); } @Override public void encodeChildren(FacesContext ctx, UIComponent uic) throws IOException { UIForeach c = (UIForeach) uic; String[] values = ((String) c.getValue()).split("\\s+"); for (int i = 0; i < values.length; i++) { Map<String, Object> attrs = ctx.getExternalContext().getRequestMap(); attrs.put(c.getVar(), values[i]); Iterator<UIComponent> it = c.getChildren().iterator(); while (it.hasNext()) { UIComponent ch = it.next(); ch.encodeAll(ctx); } } } }
- Faces config (
src/main/resources/META-INF/faces-config.xml):<?xml version="1.0" encoding="UTF-8"?> <faces-config 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_2_1.xsd" version="2.1"> <component> <component-type>com.example.Foreach</component-type> <component-class>com.example.UIForeach</component-class> </component> <render-kit> <render-kit-id>HTML_BASIC</render-kit-id> <renderer> <component-family>com.example</component-family> <renderer-type>com.example.ForeachRenderer</renderer-type> <renderer-class>com.example.ForeachRenderer</renderer-class> </renderer> </render-kit> </faces-config>
- Taglib config (
src/main/resources/META-INF/foreach.taglib.xml):<?xml version="1.0" encoding="UTF-8"?> <facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee" version="2.0"> <namespace>http://www.example.com/ns/facelettags</namespace> <tag> <tag-name>foreach</tag-name> <component> <component-type>com.example.Foreach</component-type> <renderer-type>com.example.ForeachRenderer</renderer-type> <handler-class>com.example.ForeachTagHandler</handler-class> </component> </tag> </facelet-taglib>