1

I'm working on a rich:datatable on a JSF page. The table can get pretty big and is paged with a rich:datascroller. Most of the columns are hardwired and will always be there, but then there are some optional columns based on additional values that need to be generated for each potential additional value. I've been able to make this happen easily enough. However, I'm running into a problem with filtering.

I'm using a filter on each column. It's placed in the header with the column label and sorting function. That much is working fine on each column, but I'm hitting a snag on filtering due to the way filtermethod works by default. Here's a quick example:

<rich:datatable id="thetable" value=#{backingBean.stuff} var="b">
<!-- First column, standard filter method, works just fine -->
    <rich:column sortBy="#{b.field1}" filterMethod="#{filterBean.filterField1}">
        <f:facet name="header">
            <ui:fragment>
                <h:outputText value="Field 1" />
                <h:inputText value="#{filterBean.filterMap['Field1']}" />
            </ui:fragment>
        </f:facet>
        #{b.field1}
    </rich:column>
<c:forEach items="#{backingBean.extraStuff}" var="e">
    <rich:column sortBy="#{b.getExtra(e)}" filterMethod="???">
        <f:facet name="header">
            <ui:fragment>
                <h:outputText value="#{b.getExtra(e).description}" />
                <h:inputText value="#{filterBean.filterMap['b.getExtra(e).code']}" />
            </ui:fragment>
        </f:facet>
        #{b.getExtra(e).description}
    </rich:column>
</rich:datatable>

The ??? will be covered shortly. As for the filter bean:

public class FilterBean {

    public Map<String, String> filterMap = new HashMap<String, String>();

    public boolean filterField1(Object current){
        return ((BackingBean) current).contains(filterMap.get("Field1"));
    }
}

It's fairly straightforward. The filter inputText binds to a preset string in the hashMap, which is retrieved in the method and used to filter so I don't need a separate field for every filter. This is working great, but I still need a separate method for each filter, which brings me to the ??? in the JSF code...

What I'd like to do is pass arguments to the filter method to account for the dynamic columns. In fact, I'd like to simplify the whole class with a single filter method and pass the mapped String in along with the field from the current object. However, this isn't working. I've tried:

filterMethod="#{filterBean.filterStuff(b, 'Field1')}" 

but I wind up getting the filter string just fine, but null for the current object. I'm not sure what's going on. If I'm reading the dependencies in the project correctly, I'm using some pretty old versions of EL, JSF, JSP, etc, and I really have no way of changing that. The project does use Seam, though, and I've passed arguments successfully in EL before in this project. Is it just that EL 2.2 supports passing objects while older versions only supported primitives and Strings? Is there any way for me to make this happen or am I stuck without building a ton of extra stuff from the ground up?

Okay, looks like this might be possible with Seam, but it doesn't like iteration variables. I CAN pass the object if I refer to an index in the List from the backing bean, but that doesn't help as I have no way of telling it to search every row...

user1017413
  • 2,023
  • 4
  • 26
  • 41

1 Answers1

1

My use case is a bit different, but basically I had the same problem and found a working solution.

The use case: I have several XHTML-files with their backing-beans all offering a table with a list of entities of different types. In this table there are several columns for some attributes of the entities with the possibility to filter. Since the built-in filter does only a "starts-with" search and I need a more advanced one, I have to use the filterMethod. But I did not want to mess up my backing-beans with hundreds of simple filter-methods doing all exactly the same (only with different attributes). So I was looking for a more generic way - and this is my approach:

In the backend, I created a new class named FilterMethodWrapper (for easier understanding I put it as nested static class here) and a method to access it:

import org.apache.commons.beanutils.PropertyUtils;

public class BackendBean 
{
   private String[] filterValues; 
   // initialize filterValues somewhere, add getter and setter

    public static class FilterMethodWrapper 
    {
        private final String fieldName;
        private final String filterValue;

        public FilterMethodWrapper(final String fieldName, String filterValue)
        {
            this.fieldName = fieldName;
            this.filterValue = filterValue;
        }

        public boolean doFilter(Object current) throws ...
        {
            final String stringValue = (String) PropertyUtils.getSimpleProperty(current, fieldName);
            // compare stringValue and filterValue somehow (e.g. contains)
            // and return result
        }
    }

    public FilterMethodWrapper getFilterMethodWrapper(String fieldName, int filterValueIndex)
    {
        return new FilterMethodWrapper(fieldName, getFilterValues()[filterValueIndex]);
    }
}

And in the XHTMLs use it as follows:

<rich:column filterMethod="#{backendBean.getFilterMethodWrapper('username', 0).doFilter}" filterEvent="onkeyup" >
  <f:facet name="header">
    <h:panelGrid style="display:inline;">
      <h:outputText value="Username"/>
      <h:inputText value="#{backendBean.filterValues[0]}" />
    </h:panelGrid>
  </f:facet>
  <h:outputText value="#{_item.username}" />
</rich:column>

Edit: I'm using JSF 1.2 and RichFaces 3.3.3.Final

Edit2: instead of writing the FilterMethodWrapper you could also use some Predicate-implementation and use the apply-method in the frontend (or you write your own Predicate-implementation according to this proposal which is more reusable than this FilterMethodWrapper)

MrD
  • 1,255
  • 1
  • 10
  • 24