GWT:how to change row color in GWT Celltable - gwt

i have a celltable in GWT, I can change color of a specific column by this
celltable.addColumnStyleName(4, "bluetext");
but how can i change for example color of row No 3
Thanks

You have to provide a RowStyles object that returns css class names for each row. So, to set a particular color for a row, you'd have to define a css class with that color, and then cause your RowStyles object to return that class for the relevant rows.
I think you set this with cellTable.setRowStyles or something similar.
cellTable.setRowStyles(new RowStyles<T>() {
#Override
public String getStyleNames(T rowObject, int rowIndex) {
if (rowIndex == 3) {
return "bluetext";
} else {
return "normaltext";
}
});

If you need to update row color based on a value changed in one of the cells, you can add the following code to the fieldUpdater of this cell:
#Override
public void update(int index, Object object, String value) {
if (someConditionIsMet) {
myTable.getRowElement(index).addClassName("redBackground");
}
}
In your CSS file add this style:
.redBackground {
background-color: red !important;
}

To answer the last comment that the style is in the row element but is not being rendered:
Using setRowStyles(new RowStyles() ...
The only way I got the styles to appear was to use brute force.
I had to remove the row from my List store, add it back to the same index and then refresh the RowModel.
For what it's worth.

Related

Dynamically add widgets in a cell to represent "tags" in Datagrid

In a GWT web app, I am using a DataGrid to manage elements from a database. I represent a list of elements as rows, the columns being editable fields of their characteristics (id, name, description). I am mostly using the EditTextCell class.
I now want to create a custom cell, for a column that has to represent a list of "tags" that can be attached to every element. From this cell, tags could be added, using a + button (that makes a drop-down menu appear or something), and deleted. Each tag should be a kind of button, or interactive widget (I later want to display pop-up with info, trigger actions, etc).
Actually, it would not be so different from the "tags" bar on the Stack Overflow website...
So I have been looking for a solution:
I thought this would be easy to do. I imagined just putting a FlowPanel in the cell, adding/removing Buttons/Widgets dynamically. But it turns out that in GWT Widgets and Cells and very different objects apparently..
I read making use of the AbstractCell class to create a custom cell allows to do anything, but its working is very low level and obscure to me.
I saw that CompositeCell allows to combine various cell widgets into one cell, but I have not found if it is possible to do it dynamically, or if the widgets are going to be the same for all lines throughout a column. I mostly saw examples about, for instance, how to put two Buttons in every cell of a single column.
What is the easiest way to implement what I need?
EDIT:
So, after some tests, I am going for Andrei's suggestion and going "low-level", creating a custom cell extending AbstractCell<>. I could create an appropriate "render" function, that generates a list of html "button", and also attaches Javascript calls to my Java functions when triggering a Javascript event (onclick, onmouseover, onmouseout...).
It is working pretty well. For instance, by clicking the "+" button at the end a tag list, it calls a MenuBar widget that presents the list of tags that can be added.
But I am struggling to find a way to update the underlying data when adding a tag.
To sum up:
I have a CustomData class that represents the data I want to display in each line of the table. It also contains the list of tags as a Set.
ModelTable (extends DataGrid) is my table.
CustomCell (extends AbstractCell) can renders the list of tags as several buttons on a line.
A click on a "+" button in a cell makes a AddTagMenu popup drop down, from which I can click on the tag to add.
How do I update the content of the cell?
I tried playing around with onBrowserEvent, onEnterKeyDown, bus events... with no success. At best I can indeed add a tag element to the underlying object, but the table is not updated.
It's not possible to meet your requirements without going really "low-level", as you call it.
It's relatively easy to create a cell that would render tags exactly as you want them. Plus icon is also easy, if this is the only action on the cell. However, it is very difficult to make every tag within a cell an interactive widget, because the DataGrid will not let you attach handlers to HTML rendered within a cell. You will need to supply your own IDs to these widgets, and then attach handlers to them in your code. The problem, however, is that when the DataGrid refreshes/re-renders, your handlers will most likely be lost. So you will have to attach them again to every tag in every cell on every change in the DataGrid.
A much simpler approach is to create a composite widget that represents a "row", and then add these "rows" to a FlowPanel. You can easily make it look like a table with CSS, and supply your own widget that looks like a table header. You will need to recreate some of the functionality of the DataGrid, e.g. sorting when clicked on "column" header - if you need this functionality, of course.
As you have already noted, using CompositeCell could be a way to get what you want.
The idea is to create a cell for every tag and then (during rendering) decide which one should be shown (rendered). Finally combine all those cells into one by creating a CompositeCell.
The main disadvantage of this solution is that you need to know all possible tags before you create a DataGrid.
So, if you have a fixed list of possible tags or can get a list of all existing tags and this list is reasonably small, here is a solution.
First, we need to know which tag is represented by a column so I extended a Column class to keep information about a tag. Please, note that TagColumn uses ButtonCell and also handles update when the button is clicked:
public class TagColumn extends Column<DataType, String> {
private TagEnum tag;
public TagColumn(TagEnum tag) {
super(new ButtonCell());
this.tag = tag;
setFieldUpdater(new FieldUpdater<DataType, String>() {
#Override
public void update(int index, DataType object, String value) {
Window.alert("Tag " + getTag().getName() + " clicked");
}
});
}
public TagEnum getTag() {
return tag;
}
#Override
public String getValue(DataType object) {
return tag.getName();
}
}
Then create a cell for each tag (I have hard-coded all tags in a TagEnum):
List<HasCell<DataType, ?>> tagColumns = new ArrayList<HasCell<DataType, ?>>();
for(TagEnum tag : TagEnum.values())
tagColumns.add(new TagColumn(tag));
Now, the most important part: decide either to show the tag or not - overwrite render method of the CompositeCell:
CompositeCell<DataType> tagsCell = new CompositeCell<DataType>(tagColumns) {
#Override
protected <X> void render(Context context, DataType value, SafeHtmlBuilder sb, HasCell<DataType, X> hasCell) {
if(value.getTagList().contains(((TagColumn) hasCell).getTag()))
super.render(context, value, sb, hasCell);
else
sb.appendHtmlConstant("<span></span>");
}
};
This is important to always render any element (for example empty span when the tag should not be shown). Otherwise the CompositeCell's implemantation will get confused when accessing sibling elements.
Finally, full, working example code:
private DataGrid<DataType> getGrid() {
DataGrid<DataType> grid = new DataGrid<DataType>();
List<HasCell<DataType, ?>> tagColumns = new ArrayList<HasCell<DataType, ?>>();
for(TagEnum tag : TagEnum.values())
tagColumns.add(new TagColumn(tag));
CompositeCell<DataType> tagsCell = new CompositeCell<DataType>(tagColumns) {
#Override
protected <X> void render(Context context, DataType value, SafeHtmlBuilder sb, HasCell<DataType, X> hasCell) {
if(value.getTagList().contains(((TagColumn) hasCell).getTag()))
super.render(context, value, sb, hasCell);
else
sb.appendHtmlConstant("<span></span>");
}
};
Column<DataType, DataType> tagsColumn = new Column<DataType, DataType>(tagsCell) {
#Override
public DataType getValue(DataType object) {
return object;
}
};
grid.addColumn(tagsColumn, "Tags");
grid.setRowData(Arrays.asList(
new DataType(Arrays.asList(TagEnum.gwt)),
new DataType(Arrays.asList(TagEnum.table, TagEnum.datagrid)),
new DataType(Arrays.asList(TagEnum.datagrid, TagEnum.widget, TagEnum.customCell)),
new DataType(Arrays.asList(TagEnum.gwt, TagEnum.table, TagEnum.widget, TagEnum.customCell)),
new DataType(Arrays.asList(TagEnum.gwt, TagEnum.customCell)),
new DataType(Arrays.asList(TagEnum.gwt, TagEnum.table, TagEnum.datagrid, TagEnum.widget, TagEnum.customCell))
)
);
return grid;
}
public class TagColumn extends Column<DataType, String> {
private TagEnum tag;
public TagColumn(TagEnum tag) {
super(new ButtonCell());
this.tag = tag;
setFieldUpdater(new FieldUpdater<DataType, String>() {
#Override
public void update(int index, DataType object, String value) {
Window.alert("Tag " + getTag().getName() + " clicked");
}
});
}
public TagEnum getTag() {
return tag;
}
#Override
public String getValue(DataType object) {
return tag.getName();
}
}
public class DataType {
List<TagEnum> tagList;
public DataType(List<TagEnum> tagList) {
this.tagList = tagList;
}
public List<TagEnum> getTagList() {
return tagList;
}
}
public enum TagEnum {
gwt ("gwt"),
table ("table"),
datagrid ("datagrid"),
widget ("widget"),
customCell ("custom-cell");
private String name;
private TagEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

GWT adding widget to Column Header

I want to customize my cell table's column header. I have to include support for sorting and filtering. I want to have images for both actions. When the user clicks on the sort image, it will toggle (based on ascending / descending order sort ) and the table sorts based on the icon clicked. I am currenty doing this with some messy HTML manipulation , in the onBrowserEvent of my custom header cell. Could someone tell me how I could use GWT's ToggleButton here?
Thanks.
You can't use GWT Widget in Cell Table.
But, you can use a custom Cell for the header.
public class ButtonHeader extends Header<String> {
private String text;
/**
* Construct a new TextHeader.
*
* #param text the header text as a String
*/
public ButtonHeader(String text) {
super(new ButtonCell());
this.text = text;
setUpdater(new ValueUpdater<String>() {
#Override
public void update(String value) {
//When the button is press
}
});
}
#Override
public String getValue() {
return text;
}
}
You can change this for use an other Cell for your usage.
For a (not-so) complete documentation on custom cells check this link.
You WILL have to override onBrowserEvent(), even if it's clumsy, since you can't use a GWT widget in a cell but you can render it. Yet, it'll lose all it's event handling capabilities.
(from this post) Widgets are never attached to the DOM. They exist to be manipulated in memory and then have their HTML extracted and pushed into the DOM. Events from the Widgets, therefore, are not handled. Cell events ARE handled.
So you could just use widget.getElement.getInnerHTML() to render the widget you want in your header (a toggle button or anything else). Despite having this option at hand, my advice would be to use your own SafeHtmlTemplates instead of using getInnerHTML().

How to add a css class to the first column of a CellTable widget (GWT)?

I use a CellTable widget in GWT.
I want to change the font size of every texts contained in the first column. The method addColumnStyleName does not fit my need because it adds the class to the colgroup tag; only partial css is handled on this tag, and I can't change the font attributes of a colgroup (it has no incidence on text).
So, I want to add a class on each td of the first column but cannot find an easy way to do that.
Something like setRowStyles for columns would be fine but it doesn't exist...
Have you got some hint about doing such a thing ?
EDIT: I use GWT 2.3 and not 2.4 (which has the method setCellStyleNames)
It's as easy as calling setCellStyleNames on the Column instance.
I finally get it. But I'm not really happy with that solution. It's a bit verbose for a such simple task. I'm opened to any other suggestion.
The way I achieve it is to create a new cell and a new column :
private class KeyColumn extends Column<SimpleLegendItem, String> {
public KeyColumn() {
super(new KeyCell());
}
#Override
public String getValue(SimpleLegendItem item) {
return item.getKey();
}
}
private class KeyCell extends AbstractCell<String> {
#Override
public void render(com.google.gwt.cell.client.Cell.Context context, String value, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<span class=\"" + tableResources.cellTableStyle().box() + "\">");
sb.appendEscaped(value);
sb.appendHtmlConstant("</span>");
}
}
To simplify the example, I intentionally do not use a template.

GWT ButtonCell Changing Text

How can I change ButtonCell text that's embedded in celltable column when button pressed.
I've onnly seen setFieldUpdater.
Also is there some easy way to update another CellTable column rather then accessing it directly
Cell widgets are "model-based" (MVP), you have to update the object rendered in the row (the one passed to the FieldUpdater) and then tell the CellTable that the value changed and it should redraw (use setRowData, using the index passed to the FieldUpdater).
Something like:
new FieldUpdater<MyObject, String>() {
#Override
public void update(int index, MyObject object, String value) {
object.setSomeField("foo");
cellTable.setRowData(index, Collections.singletonList(object));
}
}

GWT CellBrowser- how to always show all values?

GWT's CellBrowser is a great way of presenting dynamic data.
However when the browser contains more rows than some (seemingly) arbitrary maximum, it offers a "Show More" label that the user can click to fetch the unseen rows.
How can I disable this behavior, and force it to always show every row?
There are several ways of getting rid of the "Show More" (which you can combine):
In your TreeViewModel, in your NodeInfo's setDisplay or in the DataProvider your give to the DefaultNodeInfo, in onRangeChange: overwrite the display's visible range to the size of your data.
Extend CellBrowser and override its createPager method to return null. It won't change the list's page size though, but you can set it to some very high value there too.
The below CellBrowser removes the "Show More" text plus loads all available elements without paging.
public class ShowAllElementsCellBrowser extends CellBrowser {
public ShowAllElementsCellBrowser(TreeViewModel viewModel, CellBrowser.Resources resources) {
super(viewModel, null, resources);
}
#Override
protected <C> Widget createPager(HasData<C> display) {
PageSizePager pager = new PageSizePager(Integer.MAX_VALUE);
// removes the text "Show More" during loading
display.setRowCount(0);
// increase the visible range so that no one ever needs to page
display.setVisibleRange(0, Integer.MAX_VALUE);
pager.setDisplay(display);
return pager;
}
}
I found a valid and simple solution in setting page size to the CellBrowser's builder.
Hope this will help.
CellBrowser.Builder<AClass> cellBuilder = new CellBrowser.Builder<AClass>(myModel, null);
cellBuilder.pageSize(Integer.MAX_VALUE);
cellBrowser = cellBuilder.build();
The easiest way to do this is by using the:
cellTree.setDefaultNodeSize(Integer.MAX_VALUE);
method on your Cell Tree. You must do this before you begin expanding the tree.
My workaround is to navigate through elements of treeview dom to get "show more" element with
public static List<Element> findElements(Element element) {
ArrayList<Element> result = new ArrayList<Element>();
findShowMore(result, element); return result; }
private static void findShowMore(ArrayList res, Element element) {
String c;
if (element == null) { return; }
if (element.getInnerText().equals("Show more")) { res.add(element);
}
for (int i = 0; i < DOM.getChildCount(element); i++) { Element
child = DOM.getChild(element, i); findShowMore(res, child); } }
and than use:
if (show) { element.getStyle().clearDisplay(); } else {
element.getStyle().setDisplay(Display.NONE); }