Search code examples
sortinggwtcelltable

My CellTable does not sort


I red a lot about sorting a CellTable. I also went trough the ColumnSorting with AsyncDataProvider. But my CellTable does not sort.

Here is my code:

public class EventTable extends CellTable<Event> {

public EventTable() {

  EventsDataProvider  dataProvider = new EventsDataProvider(this);
  dataProvider.addDataDisplay(this);

  SimplePager.Resources pagerResources = GWT.create(SimplePager.Resources.class);
  SimplePager pager = new SimplePager(TextLocation.CENTER, pagerResources, false, 5, true);
  pager.setDisplay(this);

  [...]
  TextColumn<Event> nameCol = new TextColumn<Event>() {
  
    @Override
    public String getValue(Event event) {
  
      return event.getName();
    }
  };

  nameCol.setSortable(true);

  AsyncHandler columnSortHandler = new AsyncHandler(this);
  addColumnSortHandler(columnSortHandler);

  addColumn(nameCol, "Name");

  getColumnSortList().push(endCol);

  }

}


public class EventsDataProvider extends AsyncDataProvider<Event> {

  private final EventTable eventTable;

  public EventsDataProvider(EventTable eventTable) {
  
    this.eventTable = eventTable;
  }

  @Override
  protected void onRangeChanged(HasData<Event> display) {

    int start = display.getVisibleRange().getStart();
    int length = display.getVisibleRange().getLength();

    // check false values
    if (start < 0 || length < 0) return;

    // check Cache before making a rpc
    if (pageCached(start, length)) return;

    // get Events async
    getEvents(start, length);
  }
}

I do now know, if all the methods are need here. If so, I will add them. But in short:

pageCached calls a method in my PageCache Class which holds a map and a list. Before making a rpc call, the cache is checked if the events where already taken and then displayed.

getEvents just makes an rpc call via asynccallback which updates the rowdata via updateRowData() on success.

My Table is displayed fast with currently around 500 entries (could be more, depends on the customer). No missing data and the paging works fine.

I just cannot get the sorting work. As far as I know, AsyncHandler will fire a setVisibleRangeAndClearData() and then an onRangeChanged(). onRangeChanged is never fired. As for the setVisibleRangeAndClearData() I do not know. But the sortindicator (arrow next to the columnname) does change on every click.

I do not want to let the server sort the list. I have my own Comparators. It is enough, if the current visible page of the table is sorted. I do now want to sort the whole list.

Edit:

I changed following code in the EventTable constructor:

public EventTable() {
  [...]
  addColumnSortHandler(new ColumnSortEvent.AsyncHandler(this) {
  
    public void onColumnSort(ColumnSortEvent event) {
  
      super.onColumnSort(event);
      MyTextColumn<Event> myTextColumn;
    
      if (event.getColumn() instanceof MyTextColumn) {
        // Compiler Warning here: Safetytype unchecked cast
        myTextColumn = (MyTextColumn<Event>) event.getColumn();
        MyLogger.log(this.getClass().getName(), "asc " + event.isSortAscending() + " " + myTextColumn.getName(), Level.INFO);
      }
    
      List<Event> list = dataProvider.getCurrentEventList();
      if (list == null) return;
    
      if (event.isSortAscending()) Collections.sort(list, EventsComparator.getComparator(EventsComparator.NAME_SORT));
      else Collections.sort(list, EventsComparator.descending(EventsComparator.getComparator(EventsComparator.NAME_SORT)));
    }
  });
  
  addColumn(nameCol, "Name");

  getColumnSortList().push(endCol);
}

I had to write my own TextColumn to determine the Name of the column. Otherwise how should I know, which column was clicked? The page gets sorted now but I have to click twice on the column. After then, the sorting is done with every click but in the wrong order.

This solution does need polishing and it seems kinda hacky to me. Any better ideas?


Solution

  • The tutorial, that you linked to, states:

    This sorting code is here so the example works. In practice, you would sort on the server.

    Async provider is used to display data that is too big to be loaded in a single call. When a user clicks on any column to sort it, there is simply not enough objects on the client side to display "first 20 evens by name" or whatever sorting was applied. You have to go back to your server and request these first 20 events sorted by name in ascending order. And when a user reverses sorting, you have to go to the server again to get first 20 events sorted by name in a descending order, etc.

    If you can load all data in a single call, then you can use regular DataProvider, and all sorting can happen on the client side.

    EDIT:

    The problem in the posted code was in the constructor of EventsDataProvider. Now it calls onRangeChanged, and the app can load a new sorted list of events from the server.