8

In GWT 2.5 RC CellTableBuilder API was introduced but there are no comprehensive documentation available yet. Is there any tutorial\example of implementing on-demand custom row building with CellTableBuilder? The only example I've found so far was this one http://showcase2.jlabanca-testing.appspot.com/#!CwCustomDataGrid but it is quite confusing for me.

So, my goal is to create extra row containing widget that provides details about clicked row in a table.

Anton Kirillov
  • 1,306
  • 1
  • 9
  • 12

2 Answers2

6

I've found suitable solution for this problem. Here is the code sample:

public class CustomCellTableBuilder extends AbstractCellTableBuilder<Object>{
//here go fields, ctor etc.

//ids of elements which details we are going to show 
private Set elements;

@Override
protected void buildRowImpl(Object rowValue, int absRowIndex){
   //building main rows logic 

    if(elements.contains(absRowIndex)){
        buildExtraRow(absRowIndex, rowValue);
        elements.add(absRowIndex);
    }
}

private void buildExtraRow(int index, Object rowValue){
    TableRowBuilder row = startRow();
    TableCellBuilder td = row.startTD().colSpan(getColumns().size());
    DivBuilder div = td.startDiv();

    Widget widget = new Widget();

    //update widget state and appearance here depending on rowValue

    div.html(SafeHtmlUtils.fromTrustedString(widget.getElement().getInnerHTML()));

    div.end();
    td.endTD();
    row.endTR();
}}

It should be mentioned, that when you handle some event which leads to appearance of extra row, you should invoke redrawRaw(rowIndex) on CellTable which is attached to TableBuilder. And before this call it is necessary to add target row ID to elements Set.

Hope this was helpful.

Anton Kirillov
  • 1,306
  • 1
  • 9
  • 12
  • 1
    Could you show what kind of code you have on the comment "update widget state and appearance here depending on rowValue" ? Should you build the cells for the row? I was trying a simple test like seting the widget as a Label, but nothing happens. – Nuno Gonçalves Sep 26 '12 at 16:10
  • 1
    @NunoGonçalves, had you added label as `widget`'s child? Did your debugger step into `buildExtraRow(int index, Object rowValue)` method? – Anton Kirillov Sep 27 '12 at 07:35
  • 1
    @NunoGonçalves, it should be mentioned that I switched from this implementation to self-written table component which is more suitable for my needs: I need another `CellTable` as child widget and there is a lot of problems with events propagation. – Anton Kirillov Sep 27 '12 at 07:39
  • 1
    Yes, my buildExtraRow gets called. There I have this code: (I know code on comments is hard to read): TableRowBuilder row = startRow(); TableCellBuilder td = row.startTD().colSpan(getColumns().size()); DivBuilder div = td.startDiv(); Label label= new Label("HELLO WORLD"); div.html(SafeHtmlUtils.fromTrustedString(label.getElement().getInnerHTML())); div.end(); td.endTD(); row.endTR(); div object has this string builder: //
    OLAAAAAA
    . But nothing happens. I can't seem to see anything changing on the table.
    – Nuno Gonçalves Sep 27 '12 at 08:15
  • 1
    @NunoGonçalves the idea is that you attach some other child TO `widget`. try to add styled `SimplePanel` or something like that. I think there could be some issues in generating `SafeHtml` from `Label`. – Anton Kirillov Sep 27 '12 at 15:52
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/17248/discussion-between-nuno-goncalves-and-anton-kirillov) – Nuno Gonçalves Sep 27 '12 at 16:00
  • Thanks for this! Note that you must call celltable.setTableBuilder(customCellTableBuilder). Somehow I overlooked this in the documentation and assumed the wrapping constructor AbstractCellTableBuilder(AbstractCellTable cellTable) did this for me. – Naijaba Dec 16 '12 at 16:26
  • @AntonKirillov thanks for these replies, I think I came to it by looking at the GWT code at some point, we have similar code, but I noticed that adding a row through com.google.gwt.user.cellview.client.AbstractCellTableBuilder.startRow() makes that row not reacting to events (click event in my case). All the rows that were there before are ok, but new ones are not. Any ideas? – Zied Hamdi Jun 25 '14 at 10:21
0

I have created this class for expanding rows in GWT. It takes a column that you want to expand and replaces it with a place holder that can have 2 states.

I use it like this:

PlaceHolderColumn<Notification, SafeHtml> placeholder = new PlaceHolderColumn<Notification, SafeHtml>(new SafeHtmlCell()) {
        @Override
        public SafeHtml getValue(Notification object) {
            return SafeHtmlUtils.fromSafeConstant(getSelected() ? "<i class=\"glyphicon glyphicon-chevron-down\"></i>"
                    : "<i class=\"glyphicon glyphicon-chevron-right\"></i>");
        }
    };

    notificationsTable.setTableBuilder(new ExpandableCellTableBuilder<Notification, SafeHtml>(notificationsTable, columnBody, placeholder));

I have access to glyphicon so I use those instead of the default placeholder column which is +/-

< Image here... but for lack of reputation :( >

columnBody in the above code sample is just a standard column that will span the width of the table. The place holder will be displayed in its place in whatever position columnBody was configured to sit at.

Hope that helps someone :)

public class ExpandableCellTableBuilder<T, U> extends AbstractCellTableBuilder<T> {

private Column<T, U> expandColumn = null;
private PlaceHolderColumn<T, ?> placeholderColumn = null;

private final String evenRowStyle;
private final String oddRowStyle;
private final String selectedRowStyle;
private final String cellStyle;
private final String evenCellStyle;
private final String oddCellStyle;
private final String firstColumnStyle;
private final String lastColumnStyle;
private final String selectedCellStyle;

public static class ExpandMultiSelectionModel<T> extends AbstractSelectionModel<T> {

    Map<Object, T> selected = new HashMap<Object, T>();

    /**
     * @param keyProvider
     */
    public ExpandMultiSelectionModel(ProvidesKey<T> keyProvider) {
        super(keyProvider);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.google.gwt.view.client.SelectionModel#isSelected(java.lang.Object)
     */
    @Override
    public boolean isSelected(T object) {
        return isKeySelected(getKey(object));
    }

    protected boolean isKeySelected(Object key) {
        return selected.get(key) != null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.google.gwt.view.client.SelectionModel#setSelected(java.lang.Object, boolean)
     */
    @Override
    public void setSelected(T object, boolean selected) {
        Object key = getKey(object);
        if (isKeySelected(key)) {
            this.selected.remove(key);
        } else {
            this.selected.put(key, object);
        }
        scheduleSelectionChangeEvent();
    }
}

public static abstract class PlaceHolderColumn<T, C> extends Column<T, C> {

    private boolean isSelected;

    /**
     * @param cell
     */
    public PlaceHolderColumn(Cell<C> cell) {
        super(cell);
    }

    protected boolean getSelected() {
        return isSelected;
    }

}

private int expandColumnIndex;

public ExpandableCellTableBuilder(AbstractCellTable<T> cellTable, Column<T, U> expandColumn) {
    this(cellTable, expandColumn, new ExpandMultiSelectionModel<T>(cellTable.getKeyProvider()), null);
}

public ExpandableCellTableBuilder(AbstractCellTable<T> cellTable, Column<T, U> exandColumn, SelectionModel<T> selectionModel) {
    this(cellTable, exandColumn, selectionModel, null);
}

public ExpandableCellTableBuilder(AbstractCellTable<T> cellTable, Column<T, U> exandColumn, PlaceHolderColumn<T, ?> placeHolder) {
    this(cellTable, exandColumn, new ExpandMultiSelectionModel<T>(cellTable.getKeyProvider()), placeHolder);
}

/**
 * @param cellTable
 * @param columnBody
 */
public ExpandableCellTableBuilder(AbstractCellTable<T> cellTable, Column<T, U> expandColumn, SelectionModel<T> selectionModel,
        PlaceHolderColumn<T, ?> placeHolder) {
    super(cellTable);

    this.expandColumn = expandColumn;

    this.cellTable.setSelectionModel(selectionModel);

    if (placeHolder == null) {
        this.placeholderColumn = new PlaceHolderColumn<T, String>(new TextCell()) {
            @Override
            public String getValue(T object) {
                return getSelected() ? "-" : "+";
            }
        };
    } else {
        this.placeholderColumn = placeHolder;
    }

    // Cache styles for faster access.
    Style style = cellTable.getResources().style();
    evenRowStyle = style.evenRow();
    oddRowStyle = style.oddRow();
    selectedRowStyle = " " + style.selectedRow();
    cellStyle = style.cell();
    evenCellStyle = " " + style.evenRowCell();
    oddCellStyle = " " + style.oddRowCell();
    firstColumnStyle = " " + style.firstColumn();
    lastColumnStyle = " " + style.lastColumn();
    selectedCellStyle = " " + style.selectedRowCell();
}

/*
 * (non-Javadoc)
 * 
 * @see com.google.gwt.user.cellview.client.AbstractCellTableBuilder#buildRowImpl(java.lang.Object, int)
 */
@Override
protected void buildRowImpl(T rowValue, int absRowIndex) {
    // Calculate the row styles.
    SelectionModel<? super T> selectionModel = cellTable.getSelectionModel();
    final boolean isSelected = (selectionModel == null || rowValue == null) ? false : selectionModel.isSelected(rowValue);
    boolean isEven = absRowIndex % 2 == 0;
    StringBuilder trClasses = new StringBuilder(isEven ? evenRowStyle : oddRowStyle);
    if (isSelected) {
        trClasses.append(selectedRowStyle);
    }

    // Add custom row styles.
    RowStyles<T> rowStyles = cellTable.getRowStyles();
    if (rowStyles != null) {
        String extraRowStyles = rowStyles.getStyleNames(rowValue, absRowIndex);
        if (extraRowStyles != null) {
            trClasses.append(" ").append(extraRowStyles);
        }
    }

    // Build the row.
    TableRowBuilder tr = startRow();
    tr.className(trClasses.toString());

    // Build the columns.
    int columnCount = cellTable.getColumnCount();
    for (int curColumn = 0; curColumn < columnCount; curColumn++) {
        Column<T, ?> column = cellTable.getColumn(curColumn);

        if (column == expandColumn) {
            expandColumnIndex = curColumn;
            column = placeholderColumn;
            placeholderColumn.isSelected = isSelected;
        }

        // Create the cell styles.
        StringBuilder tdClasses = new StringBuilder(cellStyle);
        tdClasses.append(isEven ? evenCellStyle : oddCellStyle);
        if (curColumn == 0) {
            tdClasses.append(firstColumnStyle);
        }
        if (isSelected) {
            tdClasses.append(selectedCellStyle);
        }
        // The first and last column could be the same column.
        if (curColumn == columnCount - 1) {
            tdClasses.append(lastColumnStyle);
        }

        // Add class names specific to the cell.
        Context context = new Context(absRowIndex, curColumn, cellTable.getValueKey(rowValue));
        String cellStyles = column.getCellStyleNames(context, rowValue);
        if (cellStyles != null) {
            tdClasses.append(" " + cellStyles);
        }

        // Build the cell.
        HorizontalAlignmentConstant hAlign = column.getHorizontalAlignment();
        VerticalAlignmentConstant vAlign = column.getVerticalAlignment();
        TableCellBuilder td = tr.startTD();
        td.className(tdClasses.toString());
        if (hAlign != null) {
            td.align(hAlign.getTextAlignString());
        }
        if (vAlign != null) {
            td.vAlign(vAlign.getVerticalAlignString());
        }

        // Add the inner div.
        DivBuilder div = td.startDiv();
        div.style().outlineStyle(OutlineStyle.NONE).endStyle();

        // Render the cell into the div.
        renderCell(div, context, column, rowValue);

        // End the cell.
        div.endDiv();
        td.endTD();
    }

    // End the row.
    tr.endTR();

    if (isSelected) {
        buildExpandedRow(rowValue, absRowIndex, columnCount, trClasses, isEven, isSelected);
    }
}

/**
 * @param trClasses
 * 
 */
private void buildExpandedRow(T rowValue, int absRowIndex, int columnCount, StringBuilder trClasses, boolean isEven, boolean isSelected) {
    TableRowBuilder tr = startRow();
    tr.className(trClasses.toString());

    Column<T, ?> column = expandColumn;
    // Create the cell styles.
    StringBuilder tdClasses = new StringBuilder(cellStyle);
    tdClasses.append(isEven ? evenCellStyle : oddCellStyle);
    tdClasses.append(firstColumnStyle);
    if (isSelected) {
        tdClasses.append(selectedCellStyle);
    }
    tdClasses.append(lastColumnStyle);

    // Add class names specific to the cell.
    Context context = new Context(absRowIndex, expandColumnIndex, cellTable.getValueKey(rowValue));
    String cellStyles = column.getCellStyleNames(context, rowValue);
    if (cellStyles != null) {
        tdClasses.append(" " + cellStyles);
    }

    // Build the cell.
    HorizontalAlignmentConstant hAlign = column.getHorizontalAlignment();
    VerticalAlignmentConstant vAlign = column.getVerticalAlignment();
    TableCellBuilder td = tr.startTD();
    td.colSpan(columnCount);
    td.className(tdClasses.toString());
    if (hAlign != null) {
        td.align(hAlign.getTextAlignString());
    }
    if (vAlign != null) {
        td.vAlign(vAlign.getVerticalAlignString());
    }

    // Add the inner div.
    DivBuilder div = td.startDiv();
    div.style().outlineStyle(OutlineStyle.NONE).endStyle();

    // Render the cell into the div.
    renderCell(div, context, column, rowValue);

    // End the cell.
    div.endDiv();
    td.endTD();

    // End the row.
    tr.endTR();
}
billy1380
  • 33
  • 6