GWT CellTable tooltip not working properly - gwt

I am using a CellTable and I want to show a tooltip whenever I hover over any of the cell.
My code is working fine but the tooltip is not changing its position.
What I mean to say is, if I hover over cell 1 tooltip shows and then if I hover over cell 100, the tooltip data changes but the tooltip still shows at cell 1.
I am using following code:
cellTable.addCellPreviewHandler(new Handler<List<String>>() {
#Override
public void onCellPreview(CellPreviewEvent<List<String>> event) {
if ("mouseover".equals(event.getNativeEvent().getType())) {
Element cellElement = event.getNativeEvent().getEventTarget().cast();
cellElement.setTitle('cell contents go here.');
}
}
}
Any help is much appreciated.
Thanks.

You can extend Column class and override method render. In render method you can use property "title", which is used to set the tool tip text. Simple example:
SampleColumn<T> extends TextColumn<T> {
#Override
public void render(Context context, T object, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<div title=\"" + getTitle(object) + "\">");
sb.appendEscaped(getValue(object));
sb.appendHtmlConstant("</div>");
}
#Override
public String getValue(T object) {
return displayedValue;
}
public String getTitle(T object) {
return yourTooltipText;
}
}
When using this code, the tooltip will be exacly near the target cell.

if (cellElement.getParentElement()
.getFirstChildElement().isOrHasChild(Element.as(event.getNativeEvent().getEventTarget()))
&& cellElement.getTagName().equalsIgnoreCase("span"))
This resolves the issue.
Thanks Andrei & Alex for replies.

Thank you all, I was looking for a solution to that as well.
Using A cup of tea's answer, I made a generic class that can be reused to add tooltips to any cell. It's based on a decorator pattern. Here it is if it may help someone:
public class TooltipCell extends AbstractSafeHtmlCell<String>
{
AbstractCell<String> cell;
private String tooltip;
public TooltipCell(AbstractCell<String> cell, String tooltip)
{
super(SimpleSafeHtmlRenderer.getInstance(), BrowserEvents.CLICK, BrowserEvents.KEYDOWN);
this.cell = cell;
this.tooltip = tooltip;
}
#Override
protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb)
{
sb.appendHtmlConstant("<div title=\"" + tooltip + "\">");
cell.render(context, data.asString(), sb);
sb.appendHtmlConstant("</div>");
}
#Override
public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater)
{
cell.onBrowserEvent(context, parent, value, event, valueUpdater);
}
}
Then it can be used as follows:
myColumn = new Column<MyObject, String>(new TooltipCell(new ButtonCell(), "tooltip text"))
{
#Override
public String getValue(CurrencyDTO object)
{
return "button text";
}
};

This solution works:
cellTable.addCellPreviewHandler(new Handler<List<String>>() {
#Override
public void onCellPreview(CellPreviewEvent<List<String>> event) {
if ("mouseover".equals(event.getNativeEvent().getType())) {
cellTable.getRowElement(event.getIndex()).getCells().getItem(event.getColumn()).setTitle('cell contents go here.');
}
}
}

cellTable.addCellPreviewHandler(new Handler<List<String>>() {
#Override
public void onCellPreview(CellPreviewEvent<List<String>> event) {
if ("mouseover".equals(event.getNativeEvent().getType())) {
cellTable.getRowElement(event.getIndex()).getCells().getItem(event.getColumn()).setTitle('cell contents go here.');
}
}
}
Andrei's solution works good but there is a bug should be fixed:
cellTable.getRowElement(event.getIndex() - cellTable.getPageStart()).getCells().getItem(event.getColumn()).setTitle('cell contents go here.');

Related

Events of multiple cells in single column

I have two buttons(edit + delete) in one column.
ButtonCell functionButtonCell = new ButtonCell() {
#Override
public void render(final Context context, final SafeHtml data, final SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<button type='button' class='gwt-Button' style = 'width:60px;margin:1px;'>Edit</button>");
sb.appendHtmlConstant("<br/>");
sb.appendHtmlConstant("<button type='button' class='gwt-Button' style = 'width:60px;margin:1px;'>Delete</button>");
}
};
functionColumn = new Column<AdminModel, String>(functionButtonCell) {
public String getValue(final AdminModel object) {
return object.getSeq().toString();
}
};
Bind event for this column in Presenter as
.........
view.getFunctionColumn().setFieldUpdater(new FieldUpdater<AdminModel, String>() {
public void update(final int index, final AdminModel object, final String value) {
Window.alert(index + "-" + value);
}
});
After clicked on edit button , alert-box has appeared , but not on delete button. When I clicked on delete button , nothing has appeared. What would be the problem ?
Addition: How can I decide which button was clicked by user (edit or delete) from my presenter ?
I would really appreciate any of your suggestions because I am troubled on it for a long times. Thanks!
ButtonCell filters events on the first child element only: https://gwt.googlesource.com/gwt/+/2.6.1/user/src/com/google/gwt/cell/client/ButtonCell.java This is why you don't get an event when clicking the second button (note: the goal of that code is to make sure you clicked on the button, and not on blank space around the button; see https://gwt.googlesource.com/gwt/+/a0dc88c8be7408be9554f746eb1ec93798183a28)
The easiest way to implement a two-button cell is to use a CompositeCell; it requires that child cells are rendered into sibling elements though (uses <span>s by default, example below overrides the rendering to use <div>s so your buttons stack each on its own line).
new CompositeCell<AdminModel>(Arrays.asList(
// First button
new HasCell<AdminModel, String>() {
#Override public Cell<String> getCell() { return new ButtonCell(); }
#Override public FieldUpdated<AdminModel, String> getFieldUpdater() {
return new FieldUpdater<AdminModel, String>() {
#Override public void update(int index, AdminModel object, String value) {
Window.alert("Edit " + object.getId());
}
};
}
#Override public String getValue(AdminModel o) {
return "Edit";
}
},
// Second button
new HasCell<AdminModel, String>() {
#Override public Cell<String> getCell() { return new ButtonCell(); }
#Override public FieldUpdated<AdminModel, String> getFieldUpdater() {
return new FieldUpdater<AdminModel, String>() {
#Override public void update(int index, AdminModel object, String value) {
Window.alert("Delete " + object.getId());
}
};
}
#Override public String getValue(AdminModel o) {
return "Delete";
}
}) {
#Override protected <X> void render(Cell.Context context, AdminModel value, SafeHtmlBuilder sb, HasCell<String,X> hasCell) {
// use a <div> instead of the default <span>
Cell<X> cell = hasCell.getCell();
sb.appendHtmlConstant("<div>");
cell.render(context, hasCell.getValue(value), sb);
sb.appendHtmlConstant("</div>");
}
};
(note: in your case, because the button's text doesn't depend on the row object, maybe you should rather use an ActionCell; it would better fit "semantically" with what you're doing, but otherwise it's almost the same; with an ActionCell, you'd use HasCell<AdminModel, AdminModel>, ActionCell<AdminModel>, getFieldUpdater would return null, and thegetValueof theHasCellwould just return theAdminModel` argument as-is).
Otherwise, implement your Cell (or AbstractCell) entirely by yourself.
Ideally, a column should have only one type of cell be it ImageCell, ButtonCell etc. Because all this ImageCell and ButtonCell does not provide any in-built events. The events are handled by FieldUpdater itself which does not have differentiators to identify that which ButtonCell is clicked. Ideally on click of that column, the field-updater will be called.
You should rather create your own composite widget which extends HasCell. This composite widget will have two different buttons and those in built methods are called on click of respective button.
public void onModuleLoad() {
CellTable<Person> table = new CellTable<Person>();
List<HasCell<Person, ?>> cells = new LinkedList<HasCell<Person, ?>>();
cells.add(new ActionHasCell("Edit", new Delegate<Person>() {
#Override
public void execute(Person object) {
// EDIT CODE
}
}));
cells.add(new ActionHasCell("Delete", new Delegate<Person>() {
#Override
public void execute(Person object) {
// DELETE CODE
}
}));
CompositeCell<Person> cell = new CompositeCell<Person>(cells);
table.addColumn(new TextColumn<Person>() {
#Override
public String getValue(Person object) {
return object.getName()
}
}, "Name");
// ADD Cells for Age and Address
table.addColumn(new Column<Person, Person>(cell) {
#Override
public Person getValue(Person object) {
return object;
}
}, "Actions");
}
private class ActionHasCell implements HasCell<Person, Person> {
private ActionCell<Person> cell;
public ActionHasCell(String text, Delegate<Person> delegate) {
cell = new ActionCell<Person>(text, delegate);
}
#Override
public Cell<Person> getCell() {
return cell;
}
#Override
public FieldUpdater<Person, Person> getFieldUpdater() {
return null;
}
#Override
public Person getValue(Person object) {
return object;
}
}
Also, see the link below.
[GWT CellTable-Need to have two buttons in last single cell of each row

GWT ImageCell: Change image dynamically in a DataGrid or CellTable

I have DataGrid where one on of the columns contains images. I used this code to generate the column.
Column<Job, String> expandHideColumn = new Column<Job, String>(
imageCell) {
#Override
public String getValue(Job object) {
return null;
}
#Override
public void render(Context context, Job Object, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<img src='images/expand.jpeg' style='cursor: pointer' />");
}
}
What I want is on clicking the image it has to change. For this I added a click handler on the ImageCell like this
ImageCell imageCell = new ImageCell() {
#Override
public Set<String> getConsumedEvents() {
Set<String> events = new HashSet<String>();
events.add("click");
return events;
}
};
In the onBrowserEvent method I wrote this
#Override
public void onBrowserEvent(Context context, Element element,
Job job, NativeEvent event) {
if (element.getFirstChildElement().isOrHasChild(
Element.as(event.getEventTarget()))) {
if (element.getFirstChildElement().getPropertyString("src")
.matches("(.*)expand.jpeg")) {
element.getFirstChildElement().setPropertyString("src",
"images/collapse.jpeg");
} else {
element.getFirstChildElement().setPropertyString("src",
"images/expand.jpeg");
}
}
}
I don't think this is a good approach to change images on click event. Is there a better solution?
You can use a column value for know the state of the column :
Column<Job, Boolean> expandHideColumn = new Column<Job, Boolean>(new ImageExpandCollapseCell()) {
#Override
public Boolean getValue(Job object) {
return object.isExpand(); //The object know the expand state ?
}
}
expandHideColumn.setValueUpdater(new FieldUpdater<Job, Boolean>() {
void update(int index, Job object, Boolean value) {
object.setExpand(value);
}
});
The ImageExpandCollapseCell look like this :
public class ImageExpandCollapseCell extends AbstractCell<Boolean> {
final String EXPAND = "images/expand.jpeg";
final String COLLAPSE = "images/collapse.jpeg";
interface Template extends SafeHtmlTemplates {
#Template("<div style=\"float:right\"><img src=\"" + url + "\"></div>")
SafeHtml img(String url);
}
private static Template template;
/**
* Construct a new ImageCell.
*/
public ImageCell() {
super("click"); //Replace your getConsumedEvents()
if (template == null) {
template = GWT.create(Template.class);
}
}
#Override
public void render(Context context, Boolean value, SafeHtmlBuilder sb) {
if (value != null) {
sb.append(template.img(UriUtils.fromSafeConstant(value ? EXPAND : COLLAPSE)));
}
}
#Override
public void onBrowserEvent(Context context, Element element,
Boolean value, NativeEvent event, ValueUpdater<Boolean> valueUpdater) {
valueUpdate.update(!value);
}
}
I improve the proposed version of user905374
It's not a good idea to instantiate new value in the render method.
The column render method call the Cell render method, you musn't replace it !
With the FieldUpdater, you can change the state of the image : expand or collapse and update the cell display (it will be rendered again).

GWT: how can i add/remove a button in a celltable on the go

I have this button cell in my CellTable
ButtonCell reListCell = new ButtonCell();
reListColumn = new Column<EmployerJobs, String>(reListCell) {
#Override
public String getValue(EmployerJobs object) {
return "ReList";
}
};
ctJobs.addColumn(reListColumn,
EmployerDashBoardConstants.EMPTYHEADERCOLUMN);
but i only want this cell to be appear if the below condition pass
public void getDateDiff(final EmployerJobs object) {
rpcService.getDateDiff(object.getJobValidDate(), new AsyncCallback<Boolean>() {
public void onFailure(Throwable caught) {
}
public void onSuccess(Boolean jobExpired) {
if(jobExpired) {
// HERE I WANT TO SHOW MY RELISTCELL, means if the job is expired only then
// there will be a button showing relist would be appear in that row ,for
// the jobs which are not expired NO button should appear..
}
}
});
}
how can i achieve this?
thanks
I agree with DTing.
Quering the backend for each cell/row is not really efficient.
I would rather put the info (jobExpired) into your EmployerJobs class and transfer the info when you request the list of your EmployerJobs to be displayed in your CellTable.
You can update the list periodically to account for changes (see the expenses sample on how to do that).
But to your initial question (hiding the cell). There are two solutions:
Use an ActionCell and override the render method.
ActionCell:
ActionCell<EmployerJobs> reListCell = new ActionCell<EmployerJobs>("ReList",
new ActionCell.Delegate<EmployerJobs>() {
#Override
public void execute(EmployerJobs object) {
// code to be executed
}
})
{
#Override
public void render(Cell.Context context,EmployerJobs value,SafeHtmlBuilder sb) {
if (value.isJobExpired()) // isJobExpired returns the field jobExpired.
super.render(context,value,sb);
}
};
reListColumn = new Column<EmployerJobs, EmployerJobs>(reListCell) {
#Override
public String getValue(EmployerJobs object) {
return object;
}
};
ctJobs.addColumn(reListColumn,
EmployerDashBoardConstants.EMPTYHEADERCOLUMN);
Use a ButtonCell and override the render method of your Column.
ButtonCell:
ButtonCell reListCell = new ButtonCell();
reListColumn = new Column<EmployerJobs, String>(reListCell) {
#Override
public String getValue(EmployerJobs object) {
return "ReList";
}
#Override
public void render(Cell.Context context,EmployerJobs object,SafeHtmlBuilder sb) {
if (value.isJobExpired()) // isJobExpired returns the field jobExpired.
super.render(context,value,sb);
}
};
ctJobs.addColumn(reListColumn,
EmployerDashBoardConstants.EMPTYHEADERCOLUMN);
Just tried Umit solution #2 ButtonCell. It works!
To link an specific action to the button, reListColumn.setFieldUpdater(new FieldUpdater....
would be needed
I tried ButtonCell solution too. But if you click in a cell who as no button then an error on client side occur:
com.google.gwt.core.client.JavaScriptException: (TypeError) #com.google.gwt.core.client.impl.Impl::apply(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)([JavaScript object(445), JavaScript object(240), JavaScript object(637)]): parent is null
So I added this to override the Event and avoid Event if I want:
#Override
public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context,
Element parent, YourObject object, NativeEvent event) {
if (object.isCompleted())
super.onBrowserEvent( context, parent, object, event);
}
I don't know if it's the better way to do it but it works.

How to add clickhandler on ImageCell in GWT CellTable?

I have tried this but didn't work
Column<ContactInfo, String> imageColumn = new Column<ContactInfo, String>(new ImageCell()) {
#Override
public String getValue(ContactInfo object) {
return "contact.jpg";
}
};
imageColumn.setFieldUpdater(new FieldUpdater<ContactInfo, String>() {
#Override
public void update(int index, ContactInfo object, String value) {
Window.alert("You clicked " + object.firstName);
}
});
cellTable.addColumn(imageColumn, SafeHtmlUtils.fromSafeConstant("<br/>"));
public class ButtonImageCell extends ButtonCell{
#Override
public void render(com.google.gwt.cell.client.Cell.Context context,
String value, SafeHtmlBuilder sb) {
SafeHtml html = SafeHtmlUtils.fromTrustedString(new Image(value).toString());
sb.append(html);
}
}
in use:
final Column<ReportDTOProxy, String> buttonImageCellTest = new Column<ProxyObject, String>(new ButtonImageCell()) {
#Override
public String getValue(ProxyObject row) {
//url to image
return row.getImageUrl();
}
};
You can extend ImageCell class and override 2 it's methods - getConsumedEvents and onBrowserEvent. Example:
private class MyImageCell extends ImageCell{
#Override
public Set<String> getConsumedEvents() {
Set<String> consumedEvents = new HashSet<String>();
consumedEvents.add("dblclick");
return consumedEvents;
}
#Override
public void onBrowserEvent(Context context, Element parent,
String value, NativeEvent event,
ValueUpdater<String> valueUpdater) {
switch (DOM.eventGetType((Event)event)) {
case Event.ONDBLCLICK:
// TODO
break;
default:
break;
}
}
}
I did something similar mixing a Button cell with the renderer from an ImageCell....
ButtonCell bc = new ButtonCell() {
#Override
public void render(Context context, SafeHtml data, SafeHtmlBuilder sb) {
if (data != null) {
ImageResource icon = Icons.BUNDLE.pieChart();
SafeHtml html = SafeHtmlUtils.fromTrustedString(AbstractImagePrototype.create(icon).getHTML());
sb.append(html);
}
}
};
You get the idea. The only problem is that it does not display the "hand" icon when you hover over it.... likely it can be fixed by setting the CSS.
You can try this rather than using ButtonCell or ImageCell. This will work for sure. As I have implemented for my requirement. Let me know how does it goes..
ClickableTextCell imageCell = new ClickableTextCell() {
#Override
public void render(Context context, SafeHtml data, SafeHtmlBuilder sb) {
if (data != null) {
String imagePath = "icon.png";
sb.append(imagePath);
}
}
};
Column<ContactInfo, String> imageColumn = new Column<ContactInfo, String>(imageCell) {
#Override
public String getValue(ContactInfo object) {
return "";
}
};
imageColumn.setFieldUpdater(new FieldUpdater<ContactInfo, String>() {
#Override
public void update(int index, ContactInfo object, String value) {
Window.alert("You clicked " + object.firstName);
}
});
cellTable.addColumn(imageColumn, SafeHtmlUtils.fromSafeConstant("<br/>"));
A good solution for this issue, if you use ActionCell that is able to handle clicking. The use of it is a bit complicatied, but for me it worked pretty well.
First you need to initialize ActionCell with a delegate, in the constructor, write new ActionCell.Delegate<your class>. In this override the method execute and in that write your code that handle the clicking event.
The other thing you need to do is building up a html from the image. The SafeHtmlUtils class gives you a very easy way to do that. It's fromTrustedString method helps you building up the html:
SafeHtmlUtils.fromTrustedString(AbstractImagePrototype.create ("Your image from a resource class").getHTML());
This way the SafeHtml field can be initalized and if you give the ActionCell's contructor the SafeHtml and the Delegate, that it will do the work for you.
In this example a button will be initialized with the image from the bundle file in it. You can make it without the button if you override the render method of the ActionCell and append the SafeHtmlBuilder in the method with the same SafeHtml variable as above.
My code looks like the following:
IdentityColumn<Type> imageCell = new IdentityColumn<Type>(new ActionCell<Type>("AnyString",
new ActionCell.Delegate<Type>() {
#Override
public void execute(final Type item) {
"your code"
}
}) {
#Override
public void render(Context context, Type value, SafeHtmlBuilder sb) {
if (value != null) {
SafeHtml html = SafeHtmlUtils.fromTrustedString(AbstractImagePrototype.create(resource.image).getHTML());
sb.append(html);
}
}
});
You'd rather override the method in an another class but I didn't want to split them for this post. It worked for me very well, I hope it will help other's too.
I got all the above and added them in my app. Thanks to all. stackoverflow rocks!
ButtonCell bc = new ButtonCell() {
#Override
public void render(Context context, SafeHtml data, SafeHtmlBuilder sb) {
if (data != null) {
ImageResource icon = Connector.imageResources.minus();
Image image = new Image(icon);
//fix the mouse pointer
image.getElement().getStyle().setCursor(Cursor.POINTER);
//Do something with the DATA
image.setTitle("Delete " + data.asString());
SafeHtml html = SafeHtmlUtils.fromTrustedString(image.toString());
sb.append(html);
}
}
};
Column<InstanceProperty, String> imageColumn = new Column<InstanceProperty, String>(bc) {
#Override
public String getValue(InstanceProperty object) {
//return the DATA
return object.getKey();
}
};
imageColumn.setFieldUpdater(new FieldUpdater<InstanceProperty, String>() {
#Override
public void update(int index, InstanceProperty property,
String value) {
//you can also use the DATA to do something
InstancePropertiesTable.this.dataProvider.getList().remove(index);
}
});
addColumn(imageColumn, "");
This worked for me:
public class ButtonImageCell extends ButtonCell{
#Override
public void render(com.google.gwt.cell.client.Cell.Context context,
String renderedHtmlStr, SafeHtmlBuilder sb) {
sb.appendHtmlConstant(renderedHtmlStr);
}
}
In a class containing CellTable table:
TableResources resources = GWT.create(TableResources.class);
ImageResourceRenderer imageRenderer = new ImageResourceRenderer();
...
Column<MyRecord, String> buttonCol = new Column<MyRecord, String>(new ButtonImageCell()) {
#Override
public String getValue(MyRecord record) {
if(record.isOn())
return imageRenderer.render(resources.getOnImg()).asString();
else
return imageRenderer.render(resources.getOffImg()).asString();
}
};
buttonCol.setFieldUpdater(new FieldUpdater<MyRecord, String>() {
public void update(int index, MyRecordobject, String value) {
if (Window.confirm("Do stuff?")) {
//todo: stuff
}
}
});
...
table.addColumn(buttonCol, "");
Where the ImageResource comes from (resources):
public interface TableResources extends CellTable.Resources {
interface TableStyle extends CellTable.Style {
}
#Source("/images/on.png")
ImageResource getOnImg();
#Source("/images/off.png")
ImageResource getOffImg();
}
You need to sinkEvents() with appropriate bits.
Refer to answer of Question Adding ClickHandler to div which contains many other widget.

How can I render a ClickableTextCell as an anchor in a GWT CellTable?

I've got a CellTable with multiple columns in simple TextCell()s. Two of the columns are 'clickable' via the ClickableTextCell() class, but I want to change how they look. What's the easiest way to get the cell contents to resemble an anchor tag, while still using a cell in the table?
I've tried the following:
1. Implement a custom renderer to add anchor tags
2. Scouring Google looking for hints
3. Ignoring 'my library does it you just have to change your entire framework' links
4. Rolling my head across they keyboard
It's funny how annoying this simple change is turning out to be.
My current thought is to implement a custom AnchorCell type which puts in an Anchor widget instead of whatever it does in the other ones, but I'm not sure what all would need to be done.
Any help is appreciated.
As an example:
public class MyClickableCellText extends ClickableTextCell {
String style;
public MyClickableCellText()
{
super();
style = "myClickableCellTestStyle";
}
#Override
protected void render(Context context, SafeHtml value, SafeHtmlBuilder sb) {
if (value != null) {
sb.appendHtmlConstant("<div class=\""+style+"\">");
sb.append(value);
sb.appendHtmlConstant("</div>");
}
}
public void addStyleName(String style)
{
this.style = style;
}
}
And the style (without the div, because you are hardcoding the style on it):
.myClickableCellTestStyle{
text-decoration:underline;
}
You can even create your own cell by not extending ClickableTextCell but extending AbstractCell (more powerful but need more explanation). Ask me if you need it!
Mentioned solution have the problem, these are no links and stylesheets with a:hover and so on doesn't work.
Here is my solution:
private class SellerName {
private final String sellerName;
private final Command cmd;
private SellerName(String displayName, Command cmd) {
this.sellerName = displayName;
this.cmd = cmd;
}
public String getDisplayName() {
return sellerName;
}
public Command getCommand() {
return cmd;
}
};
private class SellerNameCell extends AbstractCell<SellerName> {
public SellerNameCell() {
super("click", "keydown");
}
#Override
public void render(com.google.gwt.cell.client.Cell.Context context, SellerName value, SafeHtmlBuilder sb) {
if (value != null) {
sb.appendHtmlConstant("<a href='javascript:;'>");
sb.appendEscaped(value.getDisplayName());
sb.appendHtmlConstant("</a>");
}
}
#Override
public void onBrowserEvent(Context context, Element parent, SellerName value, NativeEvent event, ValueUpdater<SellerName> valueUpdater) {
if (value == null)
return;
super.onBrowserEvent(context, parent, value, event, valueUpdater);
if ("click".equals(event.getType())) {
if (value.getCommand() != null)
value.getCommand().execute();
}
}
};
It creates a real anchorcell which is clickable :)
It would seem that your first instinct (implementing a custom renderer) is way easier:
SafeHtmlRenderer<String> anchorRenderer = new AbstractSafeHtmlRenderer<String>()
{
#Override
public SafeHtml render(String object) {
SafeHtmlBuilder sb = new SafeHtmlBuilder();
sb.appendHtmlConstant("<a href=\"javascript:;\">")
.appendEscaped(object).appendHtmlConstant("</a>");
return sb.toSafeHtml();
}
};
And then:
Column<YourThingy, String> anchorCol = new Column<YourThingy, String>(
new ClickableTextCell(anchorRenderer))
{
#Override
public String getValue(YourThingy object) {
return object.toString();
}
};
This is what you need:
public class ClickableSafeHtmlCell extends AbstractCell<SafeHtml> {
/**
* Construct a new ClickableSafeHtmlCell.
*/
public ClickableSafeHtmlCell() {
super("click", "keydown");
}
#Override
public void onBrowserEvent(Context context, Element parent, SafeHtml value, NativeEvent event,
ValueUpdater<SafeHtml> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
if ("click".equals(event.getType())) {
onEnterKeyDown(context, parent, value, event, valueUpdater);
}
}
#Override
protected void onEnterKeyDown(Context context, Element parent, SafeHtml value,
NativeEvent event, ValueUpdater<SafeHtml> valueUpdater) {
if (valueUpdater != null) {
valueUpdater.update(value);
}
}
#Override
public void render(Context context, SafeHtml value, SafeHtmlBuilder sb) {
if (value != null) {
sb.append(value);
}
}
And then usage:
Column<YourProxy, SafeHtml> nameColumn = new Column<YourProxy, SafeHtml>(
new ClickableSafeHtmlCell()) {
#Override
public SafeHtml getValue(YourProxy object) {
SafeHtmlBuilder sb = new SafeHtmlBuilder();
sb.appendHtmlConstant("<a>");
sb.appendEscaped(object.getName());
sb.appendHtmlConstant("</a>");
return sb.toSafeHtml();
}
};
nameColumn.setFieldUpdater(new FieldUpdater<YourProxy, SafeHtml>() {
#Override
public void update(int index, YourProxy object, SafeHtml value) {
Window.alert("You have clicked: " + object.getName());
}
});
This is actually quite simple, but it's amazing how many wrong and convoluted answers there are on Google, and seemingly no correct ones! Anyway, here's the code:
private Column<Object, SafeHtml> getAnchorColumn() {
return new Column<Object, SafeHtml>(new SafeHtmlCell()) {
#Override
public SafeHtml getValue(final Object object) {
Anchor anchor = new Anchor();
anchor.setHref(object.getURL());
anchor.setText(object.getText());
SafeHtmlBuilder sb = new SafeHtmlBuilder();
sb.appendHtmlConstant(anchor.toString());
return sb.toSafeHtml();
}
};
}
Change object to whatever it is you're trying to render in the table then run this method when creating the column, easy!
If you take a look to the clickableTextCell html code generated by gwt you will see something like (taken from gwt showcase)
<div style="outline:none;" tabindex="0">Click Robert</div>
So I will recommend u doing something like:
ClickableTextCell cell = new ClickableTextCell();
cell.addStyleName("yourStyle");
and in you style.css do whatever you want.
.yourStyle div{
text-decoration:underline;
}
It is essentially a hack to use appendHtmlConstant. Simply using toString of the Anchor or its element or passing a HTML string violates the concept behind SafeHtml entirely.
In my opinion a proper SafeHtmlTemplates should be used to tackle unreadable and unsafe string concatenation of HTML. Similar to:
protected interface AnchorTemplate extends SafeHtmlTemplates {
#Template("{1}")
SafeHtml anchor(String url, String text);
}
Then you can use GWT.create on it and interpolate the arguments properly.
Second part is that reading the HTML string of a widget could be optimized out. I was extending AbstractCell and had this method:
/* do not dare to copy - considered broken */
#Override
public void render(final Context context, final String value, final SafeHtmlBuilder sb) {
final Anchor anchor = new Anchor();
anchor.setText(value);
sb.appendHtmlConstant(anchor.getElement().toString());
}
I just experienced a case where the anchor cell was working fine in superdev mode, but compiling it (probably with more aggressive optimization settings) and then deploying it manually led to an entirely empty cell with no changes in code reproducibly across several systems. Using the template mechanism described above made it work properly (GWT 2.7).
First things first - each GWT CellTable Column is just a stack of Cell(s), as our need is to make each Cell look like an anchor which can listen to Click event lets provide ClickableTextCell as argument to Column.
Column<YourObj, String> col = new Column<YourObj, String>(new ClickableTextCell()) {};
2nd - override "render()" method in your Column instance and build your HTML template that you want, here our need is creating an anchor.
#Override
public void render(Context context, YourObj yourObj, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<a style='text-decoration:underline; cursor: pointer;'>" + yourObj.getX() + "</a>");
}
3rd - as we are using ClickableTextCell, it serves as Click Event source. We need to provide ClickEvent listener, we do that by overriding "onBrowserEvent()" method.
#Override
public void onBrowserEvent(Context context, Element elem, Customer customer, NativeEvent event) {
if ("click".equals(event.getType())) {
Window.alert("ID is : " + customer.getId());
}
}
Complete Code snippet :
Column<YourObj, String> col = new Column<YourObj, String>(new ClickableTextCell()) {
#Override
public String getValue(final YourObj yourObj) {
return yourObj.getX();
}
#Override
public void render(Context context, YourObj yourObj, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<a style='text-decoration:underline; cursor: pointer;'>" + yourObj.getX() + "</a>");
}
#Override
public void onBrowserEvent(Context context, Element elem, YourObj yourObj, NativeEvent event) {
if ("click".equals(event.getType())) {
Window.alert("ID is : " + yourObj.getId());
}
}
};
cellTable.addColumn(col, "First Name");