1

I have the following xhtml code for a JSF page:

<h:dataTable value="#{bean.data}" var="elem">
    <h:column>
        <h:panelGroup binding="#{elem.displayComponent}"/>
    </h:column>
</h:dataTable>

In my Bean I get data objects that implement an interface like this:

public interface CustomElementProvider {
    UIPanel getDisplayComponent();
    void setDisplayComponent(UIPanel container);
}

One implementation might be:

public class LabelElement implements CustomElementProvider {

    private UIPanel container;

    @Override
    UIPanel getDisplayComponent() {
        if (container == null) {
            FacesContext facesContext = FacesContext.getCurrentInstance();
            container = (UIPanel) facesContext.getApplication().createComponent(UIPanel.COMPONENT_TYPE);
            UIComponent inside = facesContext.getApplication().createComponent(HtmlOutputLabel.COMPONENT_TYPE);
            // fill label with data
            container.getChildren().add(inside);
        }
        return container;
    }

    // ... override setter
}

This works when the backing bean Bean is providing the control, but not when the unmanaged data object does this. Then I get an exception:

javax.el.PropertyNotFoundException: Target Unreachable, identifier 'elem' resolved to null

Is there a way to avoid this?

What I found so far looking around StackOverflow was mainly a situation where someone wanted to use one of two or more predefined components (see Different components in same column in datatable in JSF 2.0). This does not work, since I want to be able to extend the system by writing new data objects providing their own implementation of the component without modifying the xhtml code of the dataTable.

Another solution for my problem would be to have a different interface:

public interface CustomElementProvider {
    String getCustomComponentName();
}

And then I render the custom component given by this method. But I have no idea how to do that.

[Update] To make it more clear what I want. This is code that works:

<h:dataTable value="#{preferences.preferenceList}" var="pref">
    <h:column>
        <f:facet name="header">
            <h:outputText value="Name"/>
        </f:facet>
        <h:outputLabel id="key" value="#{pref.key}"/>
    </h:column>
    <h:column>
        <f:facet name="header">
            <h:outputText value="Value"/>
        </f:facet>
        <h:inputText id="value" value="#{pref.value}"
            rendered="#{pref.valueTypeAsString eq 'java.lang.String'
                     or pref.valueTypeAsString eq 'java.lang.Integer'}"/>
        <h:selectBooleanCheckbox id="valueBool" value="#{pref.value}"
            rendered="#{pref.valueTypeAsString eq 'java.lang.Boolean'}"/>
        <h:selectOneMenu id="valueEnum" value="#{pref.value}"
            rendered="#{pref.valueTypeAsString eq 'java.lang.Enum'}">
            <f:selectItems value="#{pref.enumEntries}"/>
        </h:selectOneMenu>
    </h:column>
</h:dataTable>

But you can see two problems here. First, when I want to add a new type of Preference that uses maybe a custom component, I would have to change (and bloat) the XHTML-code here. Also, I just don't want to modify the XHTML-code, I would like to have the system easily extendable.

Secondly, as you can see in the enum example, my interface has a method getEnumEntries() since I need it for enums. This method makes no sense for other types, but I need it here. This is not nice.

[Update/Solution] I created the entire table programmatically. Not via a UIData, because this would not help. I just add the table, tr, etc. tags plainly. This allows me to bind the edit controls nicely. As an example, this is the definition of a Boolean Preference item:

public class BooleanPreference extends PreferenceBase<Boolean> {

    public BooleanPreference(String key, Boolean defaultValue, String elName, String description) {
        super(key, defaultValue, elName, description);
    }

    @Override
    public Boolean fromString(String strValue) {
        return Boolean.valueOf(strValue);
    }

    @Override
    public UIComponent getEditComponent() {
        HtmlSelectBooleanCheckbox checkbox = new HtmlSelectBooleanCheckbox();
        checkbox.setValueExpression("value", createValueExpression(String.format("#{%s.value}", getElName())));

        return checkbox;
    }
}

The only thing that is not so nice at the moment is that I need to set an EL-name that is something like preferences.boolProp for a backing bean named Preferences with a property boolProp.

Community
  • 1
  • 1
Björn Landmesser
  • 963
  • 10
  • 26
  • 3
    Why don't you just use XHTML instead of Java to declare/create the view? XHTML is so much more intuitive and less error prone when declaring/creating the desired JSF component tree. If you really insist in using Java code for that, it would be easier if you first create and show the (working!) XHTML equivalent, then we can more easily tell you how exactly to achieve the same in Java (your concrete problem has by the way same grounds as a.o. http://stackoverflow.com/questions/3342984/; other food for reading: http://stackoverflow.com/questions/14911158/) – BalusC Apr 03 '15 at 10:04
  • @BalusC is right. If you need simple creation of components, use c:if c:choose/when in a composite component with binding. Works great. You get plain simple declaration with the option to create certain component types based an anything. – Kukeltje Apr 03 '15 at 11:57
  • @BalusC Well that is the problem. There is no working XHTML equivalent. In fact, what I want is a flexible way to edit data inside a dataTable. So if I have a String, I want to have an input field. For a boolean I want a checkbox, and for whatever may come in the future I want a matching fancy control. I can do this with xhtml by adding all components with a corresponding ``rendered`` attribute (like in http://stackoverflow.com/questions/12024382/different-components-in-same-column-in-datatable-in-jsf-2-0) But then I need to know all types and matching components beforehand. – Björn Landmesser Apr 04 '15 at 07:44

0 Answers0