3

In Vaadin 8.2, I have a Grid bound to a bean using a data provider (AbstractBackEndDataProvider). Data is fetched from a DB, filters are applied:

Grid grid = new Grid<>();
grid.setDataProvider(dataProvider.withConfigurableFilter()); // dataProvider derives from AbstractBackEndDataProvider<T,F>

The essential flow is the following: user inputs an item id in a form and submits, a submit event listener gets the user input, creates a new filter and updates the data provider:

filterSubmitButton.addClickListener(event -> {
    try {
        ItemListFilter filter = new ItemListFilter(
            itemFilter.getValue(), // itemFilter = new TextField();
        );
        filterBinder.writeBean(filter);
        dataProvider.setFilter(filter);
    } catch (ValidationException e) {
        //...
    }
 });

When the data provider gets the filter updated it calls a service to fetch new items from DB with the filter applied (to the DB query). Vaadin takes care of refreshing the Grid with new data afterwards.

What I want is to have a callback at this last moment. Say an use case would be to check if a filtered fetched result set contains only one item, to select this item in the Grid (which in its turn will trigger an event showing item details in another pane). Or to select the first grid row after initial list is loaded

But the problem is that there is neither grid.addRefreshListener() nor dataProvider.addRefreshmentListener(). Any other listeners do not seem to apply in this case.

Thanks for any help in advance.

kscherrer
  • 5,486
  • 2
  • 19
  • 59
  • Is it not possible to add your desired custom code after `dataProvider.setFilter(filter);` in your submitButton-clicklistener ? The filter should then already be in effect. – kscherrer Jan 12 '18 at 08:36
  • No, setting the filter triggers an event to fetch refreshed filtered data. You don't know at this point what's the result (to count the rows) and of course can't call select() on Grid because the grid is still filled with old (before fetching) data. – Alexander Palamarchuk Jan 12 '18 at 10:54

2 Answers2

0

The only solution I've found is a trade-off.

The item list presenter (which handles the view with the grid) passes its com.vaadin.event.EventRouter to dataProvider (I've modified the dataProvider to hold an EventRounter as a member). And now instead of streaming DB results directly from the dataProvider I fire an event that the data is fetched (using EventRouter). The presenter can subscribe to this event and then delegate it to the presenter of the details panel. There you can read the fetched results (the event contains them), check if there's only one entry and open it by id.

public class ListItemDataProvider extends AbstractBackEndDataProvider<Item, ItemFilter> {
    //...
    @Override
    protected Stream<Item> fetchFromBackEnd(Query<Item, ItemFilter> query) {
        // ...
        List<Item> fetchedResults = service.fetch(query.getOffset(), query.getLimit(), orderBy, getFilter(query));
        eventRouter.fireEvent(new FilteredDataFetchedEvent(this, fetchedResults));
        return fetchedResults.stream();
    }
}


@Controller
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ItemListPresenter {
    // ...
    public void addFilteredDataFetchedListener(ItemListView.FilteredDataFetchedListener listener) {
        eventRouter.addListener(FilteredDataFetchedEvent.class, listener, FilteredDataFetchedListener.FILTERED_DATA_FETCHED);
    }
}

Few notes:

  1. This is not exactly what I needed. Yes, I can do the most of my use case, if the filter is applied the list is reloaded and the details view gets the event to reload too. But I can't re-use the "selectionChanged" event listeners for this and actually can't select a row in the grid at all (just because the event from dataProdiver is thrown before the grid is updated).
  2. This is sort of a dirty trick, because now the dataProvider throws events and deals with eventRouters of views/presenters. On the other hand Vaadins data providers anyway do allow to subscribe on events. Using that out-of-box grid-dataProvider reactive binding we just don't have an event fired after data is fetched, so we do it in a custom way.
  3. What could also work is use the given Vaadin's subscriber dataProvider.addDataProviderListener and delegate from there an event containing the filled filter and just act independently catching that event in the details panel. But then you would need to execute sql queries twice (which can be costly) or cache them etc. This brings no benefits in comparison to the given and is still a trade-off.
0

When you invoke dataprovider.refreshAll(), the associated grid is automatially refreshed. Therefore, after following lines in your code:

filterBinder.writeBean(filter);
dataProvider.setFilter(filter);

add logic to get size of returned records (eg. dataprovider.size()) and if that equals one (01), invoke some other logic to select the one record and display its details in other panel.

Vikrant Thakur
  • 715
  • 5
  • 7