2

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

TosKen
  • 481
  • 5
  • 20
  • As the exception says, the JSF converters are used to convert from a Java value to an HTML value and vice-versa. They are always used in input components (picklists, selection menus..). What is the aim of attaching it to a column? Maybe you're looking for something like a value formatter? – Aritz Jul 17 '18 at 10:01
  • 1
    Did you try ``? – Jasper de Vries Jul 17 '18 at 10:21
  • @JasperdeVries Yes this was my first idea, but converter has no rendered attribute – TosKen Jul 17 '18 at 10:25
  • 1
    Tried adding an `` inside the `h:outputText`? – Kukeltje Jul 17 '18 at 10:32
  • @Kukeltje Did not work. There is no attribute rendered in the attribute tag. But even without this attribute the – TosKen Jul 17 '18 at 11:02
  • Ooooops, you are right, ok, what about returning a 'do-nothing' converter for the cases where there is no converter needed? Might just work. Or using the fragment, a converter inside it a 'for' attribute on the converter? Maybe it may be outside an `h:outputText` – Kukeltje Jul 17 '18 at 11:25
  • @Kukeltje unfortunately there is also no "for" attribute in the converter tag :( – TosKen Jul 17 '18 at 12:14
  • There is: https://docs.oracle.com/javaee/7/javaserver-faces-2-2/vdldocs-facelets/f/converter.html but it looks like it is (only?) for composite components. – Kukeltje Jul 17 '18 at 12:28
  • Still did not find a solution for this. The for tag does not work. Even providing a noOpConverter in case of empty conversionId did not work. I think it is not possible to define the converterId conditionally. – TosKen Jul 18 '18 at 05:29
  • no one, Any Idea? @BalusC? – TosKen Jul 19 '18 at 20:23

1 Answers1

2

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

Jasper de Vries
  • 19,370
  • 6
  • 64
  • 102