Vaadin 23 Grid: how to sync DataCommunicator with DataProvider?

202 Views Asked by At

In a Vaadin 23.3.10 Grid I'd like to select a row that is next to a row that was selected before. Therefore I read the items from the DataCommunicator. Most cases work fine, but the DataCommunicator seems to be not in sync with the DataProvider after calling dataProvider.remove(x) and dataProvider.refreshAll().

The button is plugin-based so the code snippet that selects the next row doesn't know whether some other plugin that ran before removed an item from the grid.

Here is a code example that only shows that a DataProvider and a DataCommunicator are out of sync after removing an item from the DataProvider and that the DataCommunicator is not consistent in its own:

@Route("sandbox")
public class SandboxView extends VerticalLayout {

private class Item {
    String name;
    private Item(String name) {this.name=name;}
}

public SandboxView() {
    // Generate some items
    Item a = new Item("a");
    Item b = new Item("b");
    Item c = new Item("c");
    List<Item> items = new ArrayList<>();
    items.add(a);
    items.add(b);
    items.add(c);
    ListDataProvider<Item> dataProvider = new ListDataProvider<>(items);
    
    // Create a grid
    Grid<Item> grid = new Grid<>();
    grid.setItems(dataProvider);
    grid.addColumn(item -> item.name).setHeader("Name");
    this.add(grid);
    
    // Create a button that removes an item from the grid
    Button button = new Button("Remove item A");
    button.addClickListener(event -> {
            DataCommunicator<Item> dataCommunicator = grid.getDataCommunicator();
            printDataCommunicatorItemInfo(dataCommunicator);
            // Output is as expected:
            // 3 items
            // Item: a
            // Item: b
            // Item: c
            
            dataProvider.getItems().remove(a);
            printDataCommunicatorItemInfo(dataCommunicator);
            // Output is still the same:
            // 3 items
            // Item: a
            // Item: b
            // Item: c
            
            dataProvider.refreshAll();
            printDataCommunicatorItemInfo(dataCommunicator);
            // ## UNEXPECTED ##
            // Output is out of sync:
            // 2 items
            // Item: a
            // Item: b
        }
    );
    this.add(button);
}

private void printDataCommunicatorItemInfo(DataCommunicator<Item> dataCommunicator) {
    System.out.println(dataCommunicator.getItemCount()+" items");
    for (int i=0;i<dataCommunicator.getItemCount();i++) {
        System.out.println("Item: "+dataCommunicator.getItem(i).name);
    }
}

}

The problem is that dataCommunicator.getItemCount() returns the reduced number of items (2) but dataCommunicator.getItem(...) still returns the removed item (a).

Question: How can I force the dataCommunicator to get in sync again?

2

There are 2 best solutions below

1
Jean-Christophe Gueriaud On BEST ANSWER

In Vaadin 23, you can get the next item from the GridListDataView.

For example:

GridListDataView<Person> dataView = grid.setItems(DataProvider.ofCollection(PersonUtil.buildPersons())); 
Person item; // item a 
Optional<Person> nextItem = dataView.getNextItem(item); dataView.removeItem(item); 

I'm not really sure about the dataCommunicator issue. I didn't look into the issue in details but you don't need to use it.

0
Leif Åstrand On

refreshAll() only sets a flag that the data should be recomputed but it doesn't do the actual recompute. This is a performance optimization in case refreshAll() is called multiple times in a row.

The recomputation is scheduled using UI.beforeClientResponse and those are run in the order they have been submitted for a given component. This means that you can add your own logic after the recomputation with something like UI.getCurrent().beforeClientResponse(grid, context -> printDataCommunicatorItemInfo(dataCommunicator));.