Skip to content

Commit

Permalink
fix: keep reference of selected items on arrow selection
Browse files Browse the repository at this point in the history
This helps to avoid fetching the full list of selected items from backend on each arrow press.
  • Loading branch information
paodb committed Jul 10, 2024
1 parent dbcbb1f commit 269900b
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 15 deletions.
2 changes: 1 addition & 1 deletion selection-grid-pro-flow-demo/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</organization>

<properties>
<vaadin.version>24.3.1</vaadin.version>
<vaadin.version>24.3.14</vaadin.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.vaadin.componentfactory.selectiongridpro;

import com.vaadin.componentfactory.selectiongridpro.bean.Person;
import com.vaadin.componentfactory.selectiongridpro.service.PersonData;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.provider.AbstractBackEndDataProvider;
import com.vaadin.flow.data.provider.ConfigurableFilterDataProvider;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.data.provider.QuerySortOrder;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;

/**
* Demo showing lazy data loading with filtering and sorting.
*/
@Route(value = "lazyfilter", layout = MainLayout.class)
public class LazyDataWithFilterView extends VerticalLayout {

private static final long serialVersionUID = 2692354461215796348L;

private PersonDataProvider personDataProvider = new PersonDataProvider();

private PersonFilter personFilter = new PersonFilter();

private ConfigurableFilterDataProvider<Person, Void, PersonFilter> filterDataProvider = personDataProvider
.withConfigurableFilter();

public LazyDataWithFilterView()
{
Div messageDiv = new Div();

SelectionGridPro<Person> grid = new SelectionGridPro<>();
grid.setDataProvider(filterDataProvider);

grid.addEditColumn(Person::getFirstName).text(Person::setFirstName).setHeader("First Name").setSortProperty("firstName");
grid.addEditColumn(Person::getLastName).text(Person::setLastName).setHeader("Last Name");
grid.addColumn(Person::getAge).setHeader("Age");
grid.addEditColumn(Person::isSubscriber).checkbox(Person::setSubscriber)
.setHeader("Subscriber");
grid.setSelectionMode(Grid.SelectionMode.MULTI);

grid.asMultiSelect().addValueChangeListener(event -> {
String message = String.format("Selection changed from %s to %s",
event.getOldValue(), event.getValue());
messageDiv.setText(message);
});

TextField searchField = new TextField();
searchField.setWidth("50%");
searchField.setPlaceholder("Search");
searchField.setClearButtonVisible(true);
searchField.setPrefixComponent(new Icon(VaadinIcon.SEARCH));
searchField.setValueChangeMode(ValueChangeMode.EAGER);
searchField.addValueChangeListener(e -> {
personFilter.setSearchTerm(e.getValue());
filterDataProvider.setFilter(personFilter);
});

VerticalLayout layout = new VerticalLayout(searchField, grid, messageDiv);
layout.setPadding(false);
add(layout);
}

public static class PersonFilter {

private String searchTerm;

public void setSearchTerm(String searchTerm) {
this.searchTerm = searchTerm;
}

public boolean test(Person person) {
return matches(person.getFirstName(), searchTerm);
}

private boolean matches(String value, String searchTerm) {
return searchTerm == null || searchTerm.isEmpty()
|| value.toLowerCase().contains(searchTerm.toLowerCase());
}
}

public static class PersonDataProvider extends AbstractBackEndDataProvider<Person, PersonFilter> {
PersonData data = new PersonData();
final List<Person> database = new ArrayList<>(data.getPersons());

@Override
protected Stream<Person> fetchFromBackEnd(Query<Person, PersonFilter> query) {
// A real app should use a real database or a service
// to fetch, filter and sort data.
Stream<Person> stream = database.stream();

// Filtering
if (query.getFilter().isPresent()) {
stream = stream.filter(person -> query.getFilter().get().test(person));
}

// Sorting
if (query.getSortOrders().size() > 0) {
stream = stream.sorted(sortComparator(query.getSortOrders()));
}

// Pagination
return stream.skip(query.getOffset()).limit(query.getLimit());
}

@Override
protected int sizeInBackEnd(Query<Person, PersonFilter> query) {
return (int) fetchFromBackEnd(query).count();
}

private static Comparator<Person> sortComparator(List<QuerySortOrder> sortOrders) {
return sortOrders.stream().map(sortOrder -> {
Comparator<Person> comparator = personFieldComparator(sortOrder.getSorted());

if (sortOrder.getDirection() == SortDirection.DESCENDING) {
comparator = comparator.reversed();
}

return comparator;
}).reduce(Comparator::thenComparing).orElse((p1, p2) -> 0);
}

private static Comparator<Person> personFieldComparator(String sorted) {
if (sorted.equals("firstName")) {
return Comparator.comparing(person -> person.getFirstName());
}
return (p1, p2) -> 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public MainLayout() {
final RouterLink simple = new RouterLink("Selection Grid Pro", SimpleView.class);
final RouterLink beanGrid = new RouterLink("Selection Grid Pro with a bean class", BeanGridView.class);
final RouterLink lazy = new RouterLink("Lazy Grid Pro view", LazyDataView.class);
final VerticalLayout menuLayout = new VerticalLayout(simple, beanGrid, lazy);
final RouterLink lazyFilter = new RouterLink("Lazy Grid Pro with Filter", LazyDataWithFilterView.class);
final VerticalLayout menuLayout = new VerticalLayout(simple, beanGrid, lazy, lazyFilter);
addToDrawer(menuLayout);
addToNavbar(drawerToggle);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
package com.vaadin.componentfactory.selectiongridpro;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/*
* #%L
* selection-grid-pro-flow
Expand All @@ -32,12 +24,22 @@
import com.vaadin.flow.component.gridpro.GridPro;
import com.vaadin.flow.data.provider.DataCommunicator;
import com.vaadin.flow.data.selection.SelectionModel;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Tag("vaadin-selection-grid-pro")
@CssImport(value = "./styles/grid.css", themeFor = "vaadin-selection-grid-pro")
@JsModule("./src/vcf-selection-grid-pro.js")
@JsModule("./src/selection-grid-pro.js")
public class SelectionGridPro<T> extends GridPro<T> {

private Integer selectRangeOnlyFromIndex = null;
private Set<T> selectRangeOnlySelection = new HashSet<T>();

/**
* @see Grid#Grid()
Expand Down Expand Up @@ -190,13 +192,49 @@ private Set<T> obtainNewSelectedItems(int fromIndex, int toIndex) {
private void selectRangeOnly(int fromIndex, int toIndex) {
GridSelectionModel<T> model = getSelectionModel();
if (model instanceof GridMultiSelectionModel) {
Set<T> newSelectedItems = obtainNewSelectedItems(fromIndex, toIndex);

Set<T> newSelectedItems = new HashSet<T>();

int calculatedFromIndex = fromIndex;

// selectRangeOnlySelection will keep the items already selected so there's no unnecessary
// call to backend done
if (!selectRangeOnlySelection.isEmpty()) {
int firstKey = selectRangeOnlyFromIndex;
int lastKey = selectRangeOnlySelection.size() - 1;

// recalculate from index so already selected items are not re-selected and no
// unnecessary call to backend is done
if (fromIndex == firstKey && toIndex > lastKey) {
calculatedFromIndex = lastKey + 1;
newSelectedItems.addAll(selectRangeOnlySelection);
}
}

newSelectedItems.addAll(obtainNewSelectedItems(calculatedFromIndex, toIndex));
HashSet<T> oldSelectedItems = new HashSet<>(getSelectedItems());
oldSelectedItems.removeAll(newSelectedItems);
asMultiSelect().updateSelection(newSelectedItems, oldSelectedItems);

// update selectRangeOnlySelection with new selected items
selectRangeOnlySelection = new HashSet<T>(getSelectedItems());
selectRangeOnlyFromIndex = fromIndex;
}
}


/**
* Select the range on click and makes sure selectRangeOnlySelection is cleared.
*
* @param fromIndex
* @param toIndex
*/
@ClientCallable
private void selectRangeOnlyOnClick(int fromIndex, int toIndex) {
selectRangeOnlySelection.clear();
selectRangeOnlyFromIndex = null;
this.selectRangeOnly(fromIndex, toIndex);
}

@Override
protected void setSelectionModel(GridSelectionModel<T> model, SelectionMode selectionMode) {
if (selectionMode == SelectionMode.MULTI) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function _selectionGridSelectRowWithItem(e, item, index) {
// if click select only this row
if (!ctrlKey && !e.shiftKey) {
if (this.$server) {
this.$server.selectRangeOnly(index, index);
this.$server.selectRangeOnlyOnClick(index, index);
} else {
this.selectedItems = [];
this.selectItem(item);
Expand Down Expand Up @@ -58,7 +58,7 @@ export function _selectionGridSelectRowWithItem(e, item, index) {
} else {
if (!ctrlKey) {
if (this.$server) {
this.$server.selectRangeOnly(index, index);
this.$server.selectRangeOnlyOnClick(index, index);
}
} else {
if (this.selectedItems && this.selectedItems.some((i) => i.key === item.key)) {
Expand Down Expand Up @@ -88,7 +88,7 @@ export function _selectionGridRightClickSelectRow(e) {
// keep all selected
return;
} else {
this.$server.selectRangeOnly(index, index);
this.$server.selectRangeOnlyOnClick(index, index);
}
}
}
Expand Down

0 comments on commit 269900b

Please sign in to comment.