Adding filters

Filtering should be done with the help of backend services as well. We can implement this in the same way as we did in Chapter 7Implementing CRUD User Interfaces. First, the backend service method should accept the filter input. In the example application, the filter value is a String, but in other situations, you may need a custom object containing all the values that can be used for filtering. Here is the new find method, which accepts a filter String:

public static List<Call> find(int offset, int limit, String filter,
Map<String, Boolean> sort) {
return JPAService.runInTransaction(em -> {
Query query = em.createQuery("select c from Call c where lower(c.client) like :filter or c.phoneNumber like :filter or lower(c.city) like :filter");
query.setParameter("filter",
"%" + filter.trim().toLowerCase() + "%");
query.setFirstResult(offset);
query.setMaxResults(limit);

List<Call> resultList = query.getResultList();
return resultList;
});
}

Notice how we make the filter case-insensitive by using the lower JPQL function and converting the filter value to lowercase using the toLowerCase method. We are also using the % operator to allow matches in the middle of the values in the database. We have to do something similar with the count method:

public static int count(String filter) {
return JPAService.runInTransaction(em -> {
Query query = em.createQuery("select count(c.id) from Call c where lower(c.client) like :filter or c.phoneNumber like :filter or lower(c.city) like :filter");
query.setParameter("filter", "%" + filter.trim().toLowerCase() + "%");

Long count = (Long) query.getSingleResult();
return count.intValue();
});
}

On the UI side of the implementation, we need to send the filter value to the service method. This value comes from the filter text field:

DataProvider<Call, Void> dataProvider = DataProvider.fromFilteringCallbacks(
query -> CallRepository.find(query.getOffset(), query.getLimit(),
filter.getValue()).stream(),
query -> CallRepository.count(filter.getValue())
);

We also need to refresh the DataProvider when the Search button is clicked. This can be done using a ClickListener and the refreshAll method of the DataProvider interface:

search.addClickListener(e -> dataProvider.refreshAll());

Something similar can be done for the clear button, which removes the filter introduced by the user:

clear.addClickListener(e -> {
filter.clear();
dataProvider.refreshAll();
});

When the refreshAll method is invoked, the lambda expressions we previously defined are called again and the new data is fetched from the service class.

It's generally a good idea to add database indexes to the columns the application uses to filter data. In the example application, we allow filtering on the client, phoneNumber, and city columns. You can let JPA create these indexes by using the @Index annotation, as follows:

@Entity
@Table(indexes = {
@Index(name = "client_index", columnList = "client"),
@Index(name = "phoneNumber_index", columnList = "phoneNumber"),
@Index(name = "city_index", columnList = "city")
})
@Data
public class Call {
...
}
By default, the example application generates around 500,000 rows in the Call table. Unfortunately, the Grid class cannot handle this amount of rows. See the following issues on GitHub for more information about these limitations: https://github.com/vaadin/framework/issues/6290, and https://github.com/vaadin/framework/issues/9751. One way to overcome these issues is by making the filter show results in the Grid only when the number of rows the query returns is less than an established threshold.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset