Accessing the original expression of the ValueExpression to the taglib component
Can I access the ValueExpression string passed as an attribute value to my taglib component?
My goal is to programmatically infer the missing attribute values from it. In this case, I try to avoid repeating the attribute as a literal.
Now:
<a:columnText title="name" value="#{entity.name}" sortBy="entity.name" />
:
<a:columnText title="name" value="#{entity.name}" />
-taglib.xml
<tag>
<tag-name>columnText</tag-name>
<source>column-text.xhtml</source>
<attribute>
<name>value</name>
<required>true</required>
</attribute>
<attribute>
<name>title</name>
<required>false</required>
</attribute>
<attribute>
<name>sortBy</name>
<required>false</required>
</attribute>
</tag>
columnar text.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html"
xmlns:a="http://www.kwa.nl/jsf/app-legacy" xmlns:c="http://java.sun.com/jstl/core">
<c:if test="#{empty sortBy}">
<a:expressionAsLiteral var="#{sortBy}" value="#{value}" />
</c:if>
<p:column headerText="#{title}" sortable="true" sortBy="#{sortBy}">
<h:outputText value="#{value}"/>
<a:sortByUnwrapper/>
</p:column>
</ui:composition>
<a:expressionAsLiteral />
is intended to expand ValueExpression '# {value}' to '# {entity.name}' and set '# {sortBy}' to the literal 'entity.name'. This example feeds the sort column sortBy.
public class ExpressionAsLiteral extends TagHandler {
private final TagAttribute var;
private final TagAttribute value;
public ExpressionAsLiteral(TagConfig config) {
super(config);
var = getRequiredAttribute("var");
value = getRequiredAttribute("value");
}
@Override
public void apply(FaceletContext ctx, UIComponent parent) throws IOException {
// abstracted for example.
setAsLiteral(ctx, var, unwrapFaceletAttributeValue(ctx,value));
}
}
My debugger tells me that the required information is hidden in the ValueExpressionImpl value private VariableMapper varMapper
. My problem is unpacking the returned ValueExpressionImpl without resorting to code smells.
My google-fu is failing. I feel like my approach is wrong, any advice?
EDIT # 1: Tried to do the following. Results in #{value}
instead of the desired attribute value #{iRow.title}
.
valueExpressionString = value.getValueExpression(ctx, Object.class).getExpressionString();
source to share
As for the specific question, you can access the ValueExpression
representing the value of the tag attribute as defined in the template client through FaceletContext#getVariableMapper()
and then VariableMapper#resolveVariable()
passing the tag attribute name. Then you can get the literal string of the expression via ValueExpression#getExpressionString()
.
<my:expressionAsLiteral tagAttributeName="value" />
String tagAttributeName = getRequiredAttribute("tagAttributeName").getValue();
ValueExpression ve = context.getVariableMapper().resolveVariable(tagAttributeName);
String expression = ve.getExpressionString(); // #{entity.name}
String literal = expression.substring(2, expression.length() - 1); // entity.name
However, after that it is not possible to put it in the EL scope through some "var", because PF 4.x ultimately interprets the value sortBy="#{sortBy}"
literally as #{sortBy}
, not as entity.name
. You'd better nest it inside <p:column>
and the tag handler explicitly set it.
<p:column>
<my:expressionAsLiteral tagAttributeName="value" componentAttributeName="sortBy" />
#{value}
</p:column>
String componentAttributeName = getRequiredAttribute("componentAttributeName").getValue();
parent.getAttributes().put(componentAttributeName, literal);
As far as the functional problem you're actually trying to solve, everything works fine for PF 5.2. Based on the source code, they fixed it in 5.0 and improved it in 5.1.
/WEB-INF/tags/column.xhtml
:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
>
<p:column sortBy="#{empty sortBy ? value : sortBy}">#{value}</p:column>
</ui:composition>
Template client:
<p:dataTable value="#{bean.items}" var="item">
<my:column value="#{item.id}" />
<my:column value="#{item.name}" sortBy="#{item.value}" />
<my:column value="#{item.value}" />
</p:dataTable>
source to share