NickJ
NickJ

Reputation: 9559

GWT DataGrid Headers, filterable and sortable

I am trying to extends GWT's DataGrid capabilities for my own project and would like to add the ability to filer columns. I have successfully rendered a filter box in the Header, but it is not responding to events.

Following is the relvant part of my code, which has been adapted from the code given here: CellTable with custom Header containing SearchBox and Focus Problem

The question above does not quite fit my needs, as it does not work if the columns are sortable.

Instead, I have developed a header consisted of 2 table rows (TR's), the top row containing filter boxes, the 2nd row containing column titles and responding to Sort events. The Sort events work OK, but the filter boxes to not respond to any events. Here's the code:

class HeaderBuilder extends AbstractHeaderOrFooterBuilder<Record> {

    //HTML to render an Input Box
    private InputBoxHTML inputBox = GWT.create(InputBoxHTML.class);

    //List of columns in the table
    private List<ListGridColumn<?>> columns = new ArrayList<ListGridColumn<?>>();

    //Constructor. ListGrid is the outer class extending DataGrid
    private HeaderBuilder() {
        super(ListGrid.this, false); 
    }

    @Override
    protected boolean buildHeaderOrFooterImpl() {

        TableRowBuilder tr = startRow();
        tr.startTH().endTH(); //extra column

        //Create top row of column headers - filter boxes for filterable columns, empty cells for non-filerable
        for (ListGridColumn<?> column : this.columns) {
            TableCellBuilder th = tr.startTH();
            Header<String> header;

            //If this column is filterable...
            if (column.filter) {

                //Create a new Cell containing an Input Box
                AbstractCell<String> cell = new AbstractCell<String>("click","keydown","keyup") {
                    public void render(Context context, String value, SafeHtmlBuilder sb) {
                        sb.append(inputBox.input(""));
                    }
                    public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater) {
                        //These events never fire!
                        Window.alert("event");
                    }
                };
                header = new Header<String>(cell) {
                    public String getValue() {
                        return "value";
                    }
                };

            } else {
                //Empty cell for non-filterable columns 
                header = new TextHeader("");
            }
            Context context = new Context(0, 0, header.getKey());
            renderHeader(th, context, header);
            th.endTH();
        }
        tr.endTR();

        //Bottom row : header captions & sorting. This all works OK
        tr = startRow();
        tr.startTH().endTH(); //extra column
        for (ListGridColumn<?> column : this.columns) {
            TableCellBuilder th = tr.startTH();
            enableColumnHandlers(th, column);
            Header<String> header = new TextHeader(column.headerStr);
            Context context = new Context(0, 0, header.getKey());
            if (column.sortKey!=null) {
                this.renderSortableHeader(th, context, header, true, true);
            } else {
                this.renderHeader(th, context, header);
            }
            th.endTH();
        }
        tr.endTR();

        return true;
    }

}

Upvotes: 3

Views: 3501

Answers (1)

Fedy2
Fedy2

Reputation: 3207

If you looks in the source code of insertColumn(int beforeIndex, Column col, Header header, Header footer) method in AbstractCellTable class (which is extended by DataGrid and CellTable), when a column is inserted (or added) all the events for the header (cell or footer) are sinked in order to propagate it from the table to the corresponding cell:

if (header != null) {
  Set<String> headerEvents = header.getCell().getConsumedEvents();
  if (headerEvents != null) {
    consumedEvents.addAll(headerEvents);
  }
}
...
CellBasedWidgetImpl.get().sinkEvents(this, consumedEvents);

You're declaring the Headers in the builder but not "registering" it, therefore events are not propagated to the header cell. You should find a way to register it. I don't see any clean solution because DataGrid can not be easily extended.

I can purpose you two dirty ones:

  1. Create your version of DataGrid (you need to copy and paste the code and declared it in the same package of DataGrid) and modify it in order to register two header for column.

  2. Create a new Header capable of propagate the events to the correct instance of the two headers in the column.

I will go for 2, creating an header with two cell inside, you can use these two cell in the builder.

Upvotes: 2

Related Questions