i have created a very generic jsf composite component which renders a primefaces dataTable from generic row data.
For each row which should be rendered i have a pojo named ColumnDescription which contains the header name and the row data index to get the data.
This works fine but now i have the request to allow optional converters to be set on the columns.
So i extended the ColumnDescription with a converterId. The problem is that i only have to attach the converter to the if the converterId in the ColumnDescription is not null.
First idea:
<h:outputText value="#{row[column.rowBeanProperty]}">
<c:if test="#{column.hasConverter}">
<f:converter converterId="#{column.converterId}" />
</c:if>
This did not work. The test in the if tag will not even be evaluated. I think this is because the different phases the jsp tag will be evaluated.
Second idea Use the rendered attribute for the condition
<h:outputText value="#{row[column.rowBeanProperty]}">
<ui:fragment rendered="#{column.hasConverter}">
<f:converter converterId="#{column.converterId}" />
</ui:fragment>
This does not work because the converter needs to e attached to a editable value component
TagException: /components/dataReport.xhtml @38,80 <f:converter> Parent not an instance of ValueHolder: com.sun.faces.facelets.tag.ui.ComponentRef@35b683c2
Is there any chance to add the converter conditionally?
Thanks in advance Sebastian
What you could do is write your own converter. See How create a custom converter in JSF 2?. If you know you can get a converter by calling context.getApplication().createConverter(converterId)
, it is quite simple:
@FacesConverter("optionalConverter")
public class OptionalConverter implements Converter {
private String converterId;
private boolean disabled;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return disabled
? value
: context.getApplication().createConverter(converterId).getAsObject(context, component, value);
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return null;
}
return disabled
? value.toString()
: context.getApplication().createConverter(converterId).getAsString(context, component, value);
}
// Getters and setters
}
Then create a tag for that converter. See How to create a custom Facelets tag?. So, create a file called something like my.taglib.xml
in /WEB-INF/
:
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib 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-facelettaglibrary_2_0.xsd"
version="2.0">
<namespace>http://my.com/jsf/facelets</namespace>
<tag>
<tag-name>optionalConverter</tag-name>
<converter>
<converter-id>optionalConverter</converter-id>
</converter>
<attribute>
<name>converterId</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>disabled</name>
<required>false</required>
<type>boolean</type>
</attribute>
</tag>
</facelet-taglib>
and add it to the web.xml
:
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>
Tested with:
Enabled:
<h:outputText value="1234">
<my:optionalConverter converterId="javax.faces.Number" />
</h:outputText>
Disabled:
<h:outputText value="1234">
<my:optionalConverter converterId="javax.faces.Number" disabled="true" />
</h:outputText>
Which outputted: "Enabled: 1,234 Disabled: 1234".