1
votes

I'm new to PrimeFaces and to JSF.

I need to filter a table with "contains" match mode. It works fine when the data is static, but when data changes the view doesn't update, and it still shows the old data. It updates when the user changes the filter. If no filter is applied it works fine.

Have you got any suggestions?

I post the code, inspired from the PrimeFaces 6 showcase. It is a page that displays a two columns table and a button that loads a new data for the table

Thank you, any help will be appreciated

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:p="http://primefaces.org/ui">
<h:head >
Primefaces Data Table Test
</h:head>
<h:body>
    <h:form>
        <p:dataTable var="car" value="#{dtCar.cars}" widgetVar="carsTable"
            emptyMessage="No cars found with given criteria"
            filteredValue="#{dtCar.filteredCars}">


            <p:column filterBy="#{car.model}" headerText="Model"
                footerText="contains" filterMatchMode="contains">
                <h:outputText value="#{car.model}" />
            </p:column>
            <p:column filterBy="#{car.color}" headerText="Color"
                footerText="contains" filterMatchMode="contains">
                <h:outputText value="#{car.color}" />
            </p:column>

        </p:dataTable>
        <h:commandButton id="myButton" value="Load" action="#{dtCar.loadNewCars()}"/>
    </h:form>

</h:body>
</html>

Here is the CarBean.java

package it.caditech.testtable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean(name = "dtCar")
@ViewScoped
public class CarBean {

  private List<Car> cars;

  private List<Car> filteredCars;

  public CarBean() {
    setCars(new ArrayList<Car>());
    getCars().add(new Car("myModel", "blu"));
    getCars().add(new Car("punto", "blu"));
    getCars().add(new Car("doblo", "grigio"));
    getCars().add(new Car("smart", "blu"));
    getCars().add(new Car("twingo", "rosso"));
    getCars().add(new Car("twingo", "bianco"));
    getCars().add(new Car("doblo", "blu"));
    // add more cars
  }

  // getter setter
  public List<Car> getCars() {
    return cars;
  }

  public void setCars(List<Car> cars) {
    this.cars = cars;
  }

  public List<Car> getFilteredCars() {
    return filteredCars;
  }

  public void setFilteredCars(List<Car> filteredCars) {
    this.filteredCars = filteredCars;
  }

  public String loadNewCars() {
    if (getCars().get(0).getModel().equals("myModel")) {
      getCars().clear();
      getCars().add(new Car("gt", "giallo"));
      getCars().add(new Car("scirocco", "azzurro"));
      getCars().add(new Car("duna", "giallo"));
      getCars().add(new Car("diablo", "griggio"));
      getCars().add(new Car("cayenne", "rosso"));
      getCars().add(new Car("aygo", "azzurro"));
      setFilteredCars(null);
    }
    else {
      getCars().clear();
      getCars().add(new Car("myModel", "blu"));
      getCars().add(new Car("punto", "blu"));
      getCars().add(new Car("doblo", "grigio"));
      getCars().add(new Car("smart", "blu"));
      getCars().add(new Car("twingo", "rosso"));
      getCars().add(new Car("twingo", "bianco"));
      getCars().add(new Car("doblo", "blu"));
      setFilteredCars(null);
    }
    return null;
  }
}

And here is the Car.java

package it.caditech.testtable;
public class Car {
  private String model;

  private String color;

  public Car(String m, String c) {
    model = m;
    color = c;
  }

  public String getModel() {
    return model;
  }

  public void setModel(String model) {
    this.model = model;
  }

  public String getColor() {
    return color;
  }

  public void setColor(String color) {
    this.color = color;
  }
}
4
Tried cleaning the filtered list to?Kukeltje
Thanks Kukeltje. Yes, I tried to call reset on the dataTable in action. In some sense it works. The filters are wiped out and the data is reloaded, so the datatable shows a consistent view. I'm sorry to say that it is not what users want: the filters should remain and be applied to new data on reolading.Stefano Berretta
Better to switch to using a LazyDataModel then. I do that by default. Full control over everything, no need to hassle with the 'filteredValue' etc... And If you put things in a base class, it is very reusable. And... in the mean time pay attention to github.com/omnifaces/optimusfaces very, very promising!Kukeltje
Ok, I'm gonna try with LazyDataModel. Thank youStefano Berretta
IMHO using LazyDataModel for the sole purpose of updating a filtered table, or any other purpose than lazy loading for large datasets, is a workaround for a bug in PF DataTable and defeats the purpose of the wonderful feature of automated table update after an Ajax call. I have had the same problem, tried LDM, and found the integration logic pretty onerous for a table with sorting and filtering. Of course if these features are a hard requirement then you just do it, but they are wonderful features to add, with only tiny modifications to an existing datatable, even when not required.snakedog

4 Answers

3
votes

As suggested by @Kukeltje I switch to lazyModel and it works very fine. I followed the example in Primefaces Showcase https://www.primefaces.org/showcase/ui/data/datatable/lazy.xhtml With lazy model I guess that paginator must be set to true, or else my example doesn't work.

Here's the new version of pagina1.xhtml

    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:p="http://primefaces.org/ui">
<h:head >
Primefaces Data Table Test
</h:head>
<h:body>
    <h:form id="form">
        <p:dataTable id="dt" var="car" value="#{dtCar.lazyModel}" lazy="true" paginator="true" rows="2">
            <p:column headerText="Id">
                <h:outputText value="#{car.id}" />
            </p:column>

            <p:column filterBy="#{car.model}" headerText="Model">
                <h:outputText value="#{car.model}" />
            </p:column>
            <p:column filterBy="#{car.color}" headerText="Color">
                <h:outputText value="#{car.color}"/>
            </p:column>
        </p:dataTable>
        <h:commandButton id="myButton" value="Load" action="#{dtCar.loadNewCars()}"/>
    </h:form>

</h:body>
</html>

Here is CarBean.java

package it.caditech.testtable;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

import org.primefaces.model.LazyDataModel;

@ManagedBean(name = "dtCar")
@ViewScoped
public class CarBean {

  private LazyDataModel<Car> lazyModel;

  @PostConstruct
  public void init() {
    lazyModel = new LazyCarDataModel();
  }

  public LazyDataModel<Car> getLazyModel() {
    return lazyModel;
  }

  public String loadNewCars() {
    ((LazyCarDataModel) lazyModel).loadNewCars();
    return null;
  }

}

Here is the most important part, LazyCarDataModel

package it.caditech.testtable;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;

public class LazyCarDataModel extends LazyDataModel<Car> {

  private List<Car> datasource = new ArrayList<>();

  public LazyCarDataModel() {
    loadNewCars();
  }

  @Override
  public Car getRowData(String rowKey) {
    for (Car car : datasource) {
      if (car.getId().equals(rowKey)) {
        return car;
      }
    }

    return null;
  }

  @Override
  public Object getRowKey(Car car) {
    return car.getId();
  }

  @Override
  public List<Car> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
    List<Car> data = new ArrayList<Car>();

    // filter
    for (Car car : datasource) {
      boolean match = true;

      if (filters != null) {
        for (Iterator<String> it = filters.keySet().iterator(); it.hasNext();) {
          try {
            String filterProperty = it.next();
            Object filterValue = filters.get(filterProperty);
            BeanInfo info = Introspector.getBeanInfo(Car.class);
            String fieldValue = null;
            for (PropertyDescriptor p : info.getPropertyDescriptors()) {
              if (p.getName().equals(filterProperty)) {
                fieldValue = (String) p.getReadMethod().invoke(car, new Object[0]);
              }
            }
            // String fieldValue = String.valueOf(car.getClass().getField(filterProperty).get(car));
            if (filterValue == null || fieldValue.contains(filterValue.toString())) {
              match = true;
            }
            else {
              match = false;
              break;
            }
          }
          catch (Exception e) {
            match = false;
          }
        }
      }

      if (match) {
        data.add(car);
      }
    }

    // rowCount
    int dataSize = data.size();
    setRowCount(dataSize);

    // paginate
    if (dataSize > pageSize) {
      try {
        return data.subList(first, first + pageSize);
      }
      catch (IndexOutOfBoundsException e) {
        return data.subList(first, first + dataSize % pageSize);
      }
    }
    else {
      return data;
    }
  }

  protected void loadNewCars() {
    if (datasource.isEmpty() || datasource.get(0).getModel().equals("myModel")) {
      datasource.clear();
      datasource.add(new Car("1", "gt", "giallo"));
      datasource.add(new Car("2", "scirocco", "azzurro"));
      datasource.add(new Car("3", "duna", "giallo"));
      datasource.add(new Car("4", "diablo", "griggio"));
      datasource.add(new Car("5", "cayenne", "rosso"));
      datasource.add(new Car("6", "aygo", "azzurro"));
    }
    else {
      datasource.clear();
      datasource.add(new Car("7", "myModel", "blu"));
      datasource.add(new Car("8", "punto", "blu"));
      datasource.add(new Car("9", "doblo", "grigio"));
      datasource.add(new Car("10", "smart", "blu"));
      datasource.add(new Car("11", "twingo", "rosso"));
      datasource.add(new Car("12", "twingo", "bianco"));
      datasource.add(new Car("13", "twing", "rosa"));
      datasource.add(new Car("14", "twist", "frizzante"));
      datasource.add(new Car("15", "twelf", "rose"));
      datasource.add(new Car("16", "twang", "riga"));
      datasource.add(new Car("17", "doblo", "blu"));
    }
  }
}

I guess that the car Id property and the method getRowData() and getRowKey() can be omitted, for I don't need selection handling in this case.

0
votes

Try this:

<p:dataTable id="yourDataTable" var="car"...
<p:poll interval="3" update="yourDataTable" />
0
votes

I think you are mixing two different features, filter data and show updated data all the time. For the second case, you have some options to choice. You can listen for some event and then update you data or maybe you can ask all the time for the incoming new data, like jMarcel`s example.

0
votes

check your transaction is concurrent. use the same transaction.