Search code examples
jsfjsf-2jsf-2.2

JSF conditional converter


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


Solution

  • 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".