Sunday, February 10, 2008

GWT - FlexTable with frozen header

DataGrids displaying a huge set of data with header for each column would be better usable with its header row frozen.
Plain HTML tables, with frozen header are very simple to create.

  1. Create THEAD and TBODY elements.
  2. For TBODY element's style set overflow: auto or scroll;
  3. Set fixed size for the TBODY element
In GWT, a table created using either using Grid or FlexTable, creates only TBODY element and no THEAD element.
So to render a table with frozen header, in GWT we'll have to extend either Grid or FlexTable with a THEAD element and a few getter and setter methods for accessing the THEAD columns.



package name.aanand.magix.client;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Widget;


public class DataGrid extends FlexTable {

private Element head;
private Element headerTr;

public DataGrid() {
super();
head = DOM.createTHead();
headerTr = DOM.createTR();
DOM.insertChild(this.getElement(), head, 0);
DOM.insertChild(head, headerTr, 0);
Element tBody = getBodyElement();
DOM.setElementAttribute(tBody, "style", "overflow:auto;text-align: left;");
DOM.setElementAttribute(head, "style", "text-align: left;");

}

public void setHeight(String height) {
DOM.setElementAttribute(getBodyElement(), "height", height);
}

public void setHeader(int column,String text){
prepareHeader(column);
if (text != null) {
DOM.setInnerText(DOM.getChild(headerTr, column), text);
}
}

private void prepareHeader(int column) {
if (column < 0) {
throw new IndexOutOfBoundsException(
"Cannot create a column with a negative index: " + column);
}
int cellCount = DOM.getChildCount(headerTr);
int required = column + 1 - cellCount;
if (required > 0) {
addCells(head, 0, required);
}
}


public void setHeaderWidget(int column, Widget widget) {
prepareHeader(column);
if (widget != null) {
widget.removeFromParent();
// Physical attach.
DOM.appendChild(DOM.getChild(headerTr, column), widget.getElement());

adopt(widget);
}
}

private native void addCells(Element table, int row, int num)/*-{
var rowElem = table.rows[row];
for(var i = 0; i < num; i++){
var cell = $doc.createElement("td");
rowElem.appendChild(cell);
}
}-*/;

8 comments:

Anonymous said...

Can you please explain the method below?

It looks like a syntax error.


private void prepareHeader(int column)
{
if (column < cellcount =" DOM.getChildCount(headerTr);" required =" column"> 0)
{ addCells(head, 0, required);
}
}

Unknown said...

Can you please explain the method below?

It looks like a syntax error.


private void prepareHeader(int column)
{
if (column < cellcount =" DOM.getChildCount(headerTr);" required =" column"> 0)
{ addCells(head, 0, required);
}
}

Indian Lycan said...

Hi Gaurav
I dont have any idea how it changed into some junk like that...from the source I have..
pls take a look at code that i have published now

Thanks for pointing out...

Unknown said...

Hi Aanand,

Thanks for quick reply and and updation.

My requirement is is something like I have two table header row that are frozen.

the first one is for toolbar options and second row for the column headers.

The first one will have one cell span till the last cell and it will have HorizontalPanel kind of widget.

I hope that can be done by adding one more TR

What do you say about this?

can you please share some example code which is already using DataGrid component?

Indian Lycan said...

You r right..add a TR to the head Element. i.e modify DataGrid widget with an API to add header row (TR) and it should work fine...

Unknown said...

This is really usefull, thank you!

One problem occurs though: If the scrollbar for vertical scrolling (top to bottom) is drawn a horizontal is drawn as well. Any suggestions?

AndreAgosto said...

it works just in mozilla.
it doesn't work at all in IE7

Staffan said...

Shouldn't it be createTH() for the header?