Search code examples
jsf-2primefacesdatatableclasscastexception

Primefaces datatable date range filter with filterFunction


I use filterFunction method of datatable on primefaces 5.0. I want to filter birthday by date range on column header.

On browser console I receive this error:

<?xml version="1.0"
   encoding="utf-8"?><partial-response><error><error-name>java.lang.ClassCastException</error-name><error-message><![CDATA[javax.faces.component.UIPanel
   cannot be cast to
   javax.faces.component.ValueHolder]]></error-message></error></partial-response>

Datatable :

 <p:dataTable var="person" value="#{testDateRange.persons}"
              id="personTable" paginator="true" styleClass="customTableStyle" editable="true"
              rows="10"  resizableColumns="true"
              emptyMessage="No persons"
              filteredValue="#{testDateRange.filteredPersons}"
              widgetVar="dateRangeWidget" >

     <p:column id="nameId" filterBy="name" sortBy="name" filterMatchMode="contains" headerText="Name">
         <h:outputText value="#{person.name}" />
     </p:column>

     <p:column id="birthdayId" headerText="birthday" filterBy="birthday" filterFunction="#{testDateRange.filterByDate}">
         <f:facet name="filter">
             <p:calendar id="from" value="#{testDateRange.dateFrom}"   styleClass="customCalendar" pattern="dd/MM/yyyy">
                 <p:ajax event="dateSelect" oncomplete="PF('dateRangeWidget').filter()" update="personTable"/>
             </p:calendar>
             <p:calendar id="to" value="#{testDateRange.dateTo}" styleClass="customCalendar" pattern="dd/MM/yyyy">
                 <p:ajax event="dateSelect" oncomplete="PF('dateRangeWidget').filter()" update="personTable"/>
             </p:calendar>
         </f:facet>
         <h:outputText value="#{person.birthday}"  >
             <f:convertDateTime pattern="dd/MM/yyyy"/>
         </h:outputText>
     </p:column>
 </p:dataTable>

Bean:

@Component("testDateRange")
@Scope("session")
public class TestDateRangeBean {

    private List<Person> persons;
    List<Person> filteredPersons;
    private Date dateFrom;
    private Date dateTo;

    public TestDateRangeBean() {
        persons = new ArrayList<>(Arrays.asList(
            new Person("John", new Date(1357016400000L)),
            new Person("Will",new Date(1357102800000L)),
            new Person("Peter",new Date(1414900800000L)),
            new Person("Cris", new Date(1438747200000L)),
            new Person("Cemil", new Date(1436068800000L))
        ));
    }

    public boolean filterByDate(Object value, Object filter, Locale locale) {
        // it fails before calling this method
        String filterText = (filter == null) ? null : filter.toString().trim();
        if(StringUtils.isEmpty(filterText)) {
            return true;
        }
        if(value == null) {
            return false;
        }
        DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
        Date filterDate;
        try {
            filterDate = df.parse(filterText);
        } catch (ParseException e) {
            return false;
        }
        return filterDate.after(dateFrom) &&  filterDate.before(dateTo);
    } 
    //all the getters and setters

enter image description here

And I have a simple POJO Person class that contains only 'name' and 'birthday'.

How can I filter birthday by the dates I enter on column header. I see that it gets the UIPanel component instead of dates values. (I assume it expects one component with a value, and when it finds two components, it returns the container component itself, am I right?)

Thanks


Solution

  • Advanced solution:

    JSF:

    <p:column headerText="#{msg.date}" sortBy="#{bean.date}" filterBy="#{bean.date}" filterFunction="#{dateRangeFilter.filterByDate}">
      <f:facet name="filter">
        <h:inputHidden id="filter" />
      </f:facet>
      <f:facet name="header">
        <h:outputText value="#{msg.date}" />
        <br />
        <p:calendar id="from" pattern="dd.MM.yyyy">
          <p:ajax event="dateSelect" onstart="$(PrimeFaces.escapeClientId('#{p:component('filter')}'))[0].value = $(PrimeFaces.escapeClientId('#{p:component('from')}_input'))[0].value + '-' + $(PrimeFaces.escapeClientId('#{p:component('to')}_input'))[0].value" oncomplete="PF('dataTable').filter()" />
        </p:calendar>
        <p:calendar id="to" pattern="dd.MM.yyyy">
          <p:ajax event="dateSelect" onstart="$(PrimeFaces.escapeClientId('#{p:component('filter')}'))[0].value = $(PrimeFaces.escapeClientId('#{p:component('from')}_input'))[0].value + '-' + $(PrimeFaces.escapeClientId('#{p:component('to')}_input'))[0].value" oncomplete="PF('dataTable').filter()" />
        </p:calendar>
      </f:facet>
      <h:outputText value="#{bean.date}">
        <f:convertDateTime type="date" dateStyle="medium" />
      </h:outputText>
    </p:column>
    

    Filter Bean:

    @Named
    @ApplicationScoped
    public class DateRangeFilter implements Serializable {
    
        private static final Logger LOG = Logger.getLogger(DateRangeFilter.class.getName());
    
        public boolean filterByDate(Object value, Object filter, Locale locale) {
    
            String filterText = (filter == null) ? null : filter.toString().trim();
            if (filterText == null || filterText.isEmpty()) {
                return true;
            }
            if (value == null) {
                return false;
            }
    
            DateFormat df = SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM);
    
            Date filterDate = (Date) value;
            Date dateFrom;
            Date dateTo;
            try {
                String fromPart = filterText.substring(0, filterText.indexOf("-"));
                String toPart = filterText.substring(filterText.indexOf("-") + 1);
                dateFrom = fromPart.isEmpty() ? null : df.parse(fromPart);
                dateTo = toPart.isEmpty() ? null : df.parse(toPart);
            } catch (ParseException pe) {
                LOG.log(Level.SEVERE, "unable to parse date: " + filterText, pe);
                return false;
            }
    
            return (dateFrom == null || filterDate.after(dateFrom)) && (dateTo == null || filterDate.before(dateTo));
        }
    }