Mixing custom tag with facelet ui taglibs
November 21, 2011 15:57:49 Last update: November 22, 2011 09:51:26
The improved custom taglib works with existing facelet ui taglibs. For example:
produces the expected output.
However, a problem exists with the
When tested with a URL like:
the raw EL prints out the correct names, but my custom tag substitutes empty string for
In theory, the response is rendered in the Render Response phase, way after the Apply Request Values phase, actual values should be available to EL. The answer to this anomaly turned out to be very deep! Yes, right there in the code! I would consider this a bug in facelets implementation, but the JSF spec did not tell what the expected behavior should be.
In my custom tag, the EL is evaluated when
To make our custom tag work with
Alternative implementation:
<ui:param name="theName" value="John"/> <my:hello name="#{theName}"/>
produces the expected output.
However, a problem exists with the
ui:repeat tag:
<h3>With ui:repeat</h3> <ui:repeat var="theName2" value="#{paramValues['name']}"> <b>My Tag:</b> <my:hello name="#{theName2}"/> <p><b>Raw EL:</b> #{theName2}</p> </ui:repeat>
When tested with a URL like:
http://localhost:8080/facelet-demo/?name=Zack&name=John
the raw EL prints out the correct names, but my custom tag substitutes empty string for
theName2!
In theory, the response is rendered in the Render Response phase, way after the Apply Request Values phase, actual values should be available to EL. The answer to this anomaly turned out to be very deep! Yes, right there in the code! I would consider this a bug in facelets implementation, but the JSF spec did not tell what the expected behavior should be.
In my custom tag, the EL is evaluated when
apply() is called, but the ui:repeat tag sets the variable in encodeChildren(), which happens after apply() returns.
To make our custom tag work with
ui:repeat, we need to delay the EL evaluation. Change the HelloTagHandler to:
package com.example; import java.io.IOException; import javax.faces.component.UIComponent; import javax.faces.component.UIComponentBase; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.view.facelets.FaceletContext; import javax.faces.view.facelets.TagAttribute; import javax.faces.view.facelets.TagHandler; import javax.faces.view.facelets.TagConfig; /** * Custom facelet tag that says hello. */ public class HelloTagHandler extends TagHandler { private String name = "Anonymous"; public HelloTagHandler(TagConfig config) { super(config); } public void apply(final FaceletContext context, UIComponent parent) throws IOException { final TagAttribute a = tag.getAttributes().get("name"); UIComponentBase c = new UIComponentBase() { public void encodeEnd(FacesContext ctx) throws IOException { if (a != null) { name = a.getValue(context); } ResponseWriter w = ctx.getResponseWriter(); w.write(String.format( "<p>Hello %s! I am FaceletTag.</p>", name )); } // abstract method in base, must override public String getFamily() { return "com.example.facelettag.test"; } }; parent.getChildren().add(c); } }
Alternative implementation:
package com.example; import java.io.IOException; import javax.el.ValueExpression; import javax.faces.component.UIComponent; import javax.faces.component.UIComponentBase; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.view.facelets.FaceletContext; import javax.faces.view.facelets.TagAttribute; import javax.faces.view.facelets.TagHandler; import javax.faces.view.facelets.TagConfig; /** * Custom facelet tag that says hello. */ public class HelloTagHandler extends TagHandler { private TagAttribute nameAttr; public HelloTagHandler(TagConfig config) { super(config); nameAttr = getAttribute("name"); } public void apply(FaceletContext context, UIComponent parent) throws IOException { final ValueExpression vex = (this.nameAttr == null) ? null : this.nameAttr.getValueExpression( context, Object.class ); UIComponentBase c = new UIComponentBase() { public void encodeEnd(FacesContext ctx) throws IOException { ResponseWriter w = ctx.getResponseWriter(); w.write(String.format( "<p>Hello %s! I am FaceletTag.</p>", (vex == null) ? "Anonymous" : vex.getValue(ctx.getELContext()) )); } // abstract method in base, must override public String getFamily() { return "com.example.facelettag.test"; } }; parent.getChildren().add(c); } }