<h:dataTable value="#{mybean.myData}" var="row"
styleClass="portlet-section-body"
rowClasses="portlet-section-body,portlet-section-alternate"
headerClass="portlet-section-header">
So, I'd like to replace that with a facelet instead, so that it looks like this:
<p:dataTable value="#{mybean.myData}" var="row">
And the facelet would be defined once in a .xhtml file as:
<ui:composition>
<h:dataTable value="#{value}" var="#{var}"
styleClass="portlet-section-body"
rowClasses="portlet-section-body,portlet-section-alternate"
headerClass="portlet-section-header">
<ui:insert />
</h:dataTable>
</ui:composition>
However, this doesn't quite work. The problem is that it tries to set the var attribute of the dataTable component to a value expression, but dataTable requires a simple string for var. So we have to figure out some way of getting the var variable from the facelet into the dataTable component. What I came up with was a custom TagHandler that evaluates the value expression and then sets the var property of the dataTable component directly:
package portletfacelets;
import java.io.IOException;
import javax.el.ELException;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlDataTable;
import com.sun.facelets.FaceletContext;
import com.sun.facelets.FaceletException;
import com.sun.facelets.tag.TagAttribute;
import com.sun.facelets.tag.TagConfig;
import com.sun.facelets.tag.TagHandler;
public class SetVarHandler extends TagHandler {
private final TagAttribute var;
public SetVarHandler(TagConfig config) {
super(config);
this.var = this.getAttribute("var");
}
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, FaceletException, ELException {
System.out.println("var=" + this.var.getValue(ctx));
if (parent instanceof HtmlDataTable) {
HtmlDataTable table = (HtmlDataTable) parent;
table.setVar(this.var.getValue(ctx));
}
this.nextHandler.apply(ctx, parent);
}
}
I add this to my facelets taglib.xml file as such:
<tag>
<tag-name>setVar</tag-name>
<handler-class>portletfacelets.SetVarHandler</handler-class>
</tag>
And then use it like this in my facelet:
<ui:composition>
<h:dataTable value="#{value}"
styleClass="portlet-section-body"
rowClasses="portlet-section-body,portlet-section-alternate"
headerClass="portlet-section-header">
<p:setVar var="#{var}" />
<ui:insert />
</h:dataTable>
</ui:composition>
Voila! However, seeing as this might come in handy in future instances, I wondered if I couldn't make something a bit more generic. So I created this TagHandler:
package portletfacelets;
import java.io.IOException;
import java.lang.reflect.Method;
import javax.el.ELException;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import com.sun.facelets.FaceletContext;
import com.sun.facelets.FaceletException;
import com.sun.facelets.tag.TagAttribute;
import com.sun.facelets.tag.TagConfig;
import com.sun.facelets.tag.TagHandler;
public class SetValueHandler extends TagHandler {
private final TagAttribute methodName;
private final TagAttribute value;
public SetValueHandler(TagConfig config) {
super(config);
this.methodName = this.getRequiredAttribute("methodName");
this.value = this.getRequiredAttribute("value");
}
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, FaceletException, ELException {
try {
Method m = parent.getClass().getMethod(this.methodName.getValue(ctx),
new Class[]{String.class});
m.invoke(parent, new Object[]{this.value.getValue(ctx)});
} catch (Exception e) {
e.printStackTrace();
}
this.nextHandler.apply(ctx, parent);
}
}
So I'm using reflection to call any method on the parent JSF component and passing in the specified value. Then I use it in my facelet like so:
<ui:composition>
<h:dataTable value="#{value}"
styleClass="portlet-section-body"
rowClasses="portlet-section-body,portlet-section-alternate"
headerClass="portlet-section-header">
<p:setValue methodName="setVar" value="#{var}" />
<ui:insert />
</h:dataTable>
</ui:composition>
There you have it. Turns out there is more than one way to skin this cat, so I'll also share in a future blog post a different approach to this issue.
8 comments:
Thank you, I was having the same problem and you gave me an elegant solution!
hi..
have u ever tried 2 datatable facelets composition in 1 xhtml ?
for example..
< k:table id="x" value="null" >
< k:column title="A" >
< h:outputText value="a"/ >
< /k:column >
< /k:table >
< k:table id="y" value="null" >
< k:column title="B" >
< h:outputText value="b"/ >
< /k:column >
< /k:table >
renders 1 table with 2 columns(A,B) and 1 row (a,b) ??
any idea ?
thanks..
Thankyou! This solution saved me a lot of mucking around!
Just a heads up for anybody trying to wrap a RichData table, if you use the SetVarHandler code, be sure you import org.richfaces.component.html.HtmlDataTable instead of the JSF one. Otherwise the instanceof will evaluate to false.
I've enhanced the code a bit more - I didn't like the reflection approach, because it relies on knowing the java API for the tag you are trying to modify. Instead I found you can just change the attribute on the tag directly like so:
public class SetTagAttributeHandler extends TagHandler {
private TagAttribute value;
private TagAttribute attribute;
public SetTagAttributeHandler(TagConfig config) {
super(config);
this.value = this.getAttribute("value");
this.attribute = this.getAttribute("attribute");
}
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, FaceletException, ELException {
if (parent instanceof org.richfaces.component.html.HtmlDataTable) {
org.richfaces.component.html.HtmlDataTable table = (
org.richfaces.component.html.HtmlDataTable) parent;
table.getAttributes().put(attribute.getValue(ctx), value.getValue(ctx));
}
this.nextHandler.apply(ctx, parent);
}
}
No reflection needed or knowledge of the tag java classes needed! This is helpful because the are a number of attributes that it would be useful to set to EL, but you cannot.
A step further from paradox's code, no need to cast or to limit the functionality to datatable.
public class SetTagAttributeHandler extends TagHandler {
private final TagAttribute attribute;
private final TagAttribute value;
public SetTagAttributeHandler(TagConfig config) {
super(config);
this.attribute = this.getAttribute("attribute");
this.value = this.getAttribute("value");
}
@Override
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, FaceletException, ELException {
parent.getAttributes().put(attribute.getValue(ctx), value.getValue(ctx));
this.nextHandler.apply(ctx, parent);
}
Post a Comment