how to clear SafeHtmlBuilder value - gwt

I am using SafeHtmlBuilder i my GWT/GXT application ,
We have set someloading icon in it and then later in few seconds i like to remove that loading icon and display some other icon.
But i am not able to clear that loading icon
is there a way ..
render(Context context, SymbolDTO value, SafeHtmlBuilder sb)
{
sb.appendHtmlConstant("<div style=\"cursor: pointer;\"> " + symbolStr2 + "</div>");
sb.appendHtmlConstant("<div style=\"cursor: pointer;\"> " + symbolStr3 + "</div>");
so when i insert symbolstr3, it displays symbol2 and symbol3 , but how i can first remove symbol2 and only display symbol3 here..

The correct way is create a new SafeHtmlBuilder instance when you want to start fresh, SafeHtmlBuilder isnt a widget, is it from its name a way to cosntruct html string literals to be used later as html content for other widgets or elements.
So in your case depending on from where you are passing that SafeHtmlBuilder you might better return a new builder instance in the that method, or pass a new instance of SafeHtmlBuilder, and when you execute that method you simply clear the target element and set its content with the html from the builder by calling the toSafeHtml and get the html string from it.

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;
}
}

use cellTree with widget

I want to add customized widgets into the cell tree, something like below:
>Label1
customized-widget
>Label2
customized-widget
>Label3
customized-widget
Under each label, there is a customized widget(extends composite)
I tried to use
public void render(
com.google.gwt.cell.client.Cell.Context context, Customizedwidget value, SafeHtmlBuilder sb) {
if(value!=null) sb.appendEscaped(value.getElement().getInnerHTML());
}
but, sb.appendEscaped(value.getElement().getInnerHTML()) is not working, it shows me a bunch of html or maybe javascript code when I click label.
I would like to know how I can solve this problem?
You can use SafeHtmlCell to include your own html content inside of the cell table's cell,
SafeHtmlCell html = new SafeHtmlCell();
final Column<DTO, SafeHtml> htmlContent = new Column<DTO, SafeHtml>(html) {
#Override
public SafeHtml getValue(DTO object) {
// code goes here
return new SafeHtml;
}
};
dataGrid.addColumn(htmlContent, "");
or you can use ButtonCell(to insert buttons), check other cell's also.
check this: GWT - Make CellTable Cell use HTML?

Can SafeHtmlBuilder excapse SafeHtml while still keeping SafeHtml the way it is(GWT)?

Here is my requirement. I have a textbox & user can type anything in the textbox and the system will convert it into html. However, it only converts <b> or <i> tag & ignore all other tags. The result of that text will be put into <h1> tag.
Ex, user typ in textbox: <h1>text <i>test</i></h1> it will out put:
<h1>text test</h1>
So if user types <h1> in the text box then the system should be smart enough to know that it should escape that <h1> but then it has to put <h1> to the final string.
The code could be like this. I use SimpleHtmlSanitizer to sanitize string & only allows <b> or <i>
SafeHtml mySafeHtml=MySimpleHtmlSanitizer.sanitizeHtml("<h1>text <i>test</i></h1>");
so, if i print out mySafeHtml, then it will show like this:
<h1>text test</h1>
But Then how to make that String embraced inside tag?
SafeHtmlBuilder builder = new SafeHtmlBuilder();
builder.appendHtmlConstant("<h1>");
builder.appendEscaped(mySafeHtml); // err here cos SafeHtmlBuilder does not excapse SafeHtml?
builder.appendHtmlConstant("</h1>");
So how to solve my problem?
My take on this would be something like this, why not check if your mySafeHtml starts with a <h1> and then conditionally append?
Example :
SafeHtmlBuilder builder = new SafeHtmlBuilder();
//check if your `mySafeHtml` starts and ends with <h1>
if( (mySafeHtml.startsWith("<h1>")
|| mySafeHtml.startsWith("<H1>"))
&& (mySafeHtml.endsWith("</h1>")
|| mySafeHtml.endsWith("</H1>"))
)
{
builder.appendEscaped(mySafeHtml); // err here cos SafeHtmlBuilder does not excapse
}
else
{
builder.appendHtmlConstant("<h1>");
builder.appendEscaped(mySafeHtml); // err here cos SafeHtmlBuilder does not excapse SafeHtml?
builder.appendHtmlConstant("</h1>");
}

gwt Using safehtml in window.settitle for setting browser title

I need to send a SafeHtml to Window.setTitle().
The problem is that i use SafeHtmlUtils.fromString(String c) on the title.
Due to chance of XSS Leaks.
The result is that i get a bad looking title (due to the conversion SafeHtmlUtils.fromString to HTML Entity References)
My question is can you set the Window title with a SafeHtml?
Or how do i convert the Safehtml back to normal String?
code (updated):
public void setTitle(SafeHtml title) {
internalHeader.setPageTitle(title);
Window.setTitle(("fileee | " + title).replaceAll("\\<.*?>", ""));
}
//in internalHeader
public void setPageTitle(SafeHtml title) {
pageNameBig.setInnerSafeHtml(title);
}
pageNameBig is a HeadingElement.
Problem is that if i put in <h1>test the result is in Window.setTitle = <h1>test
but in the internalHeader is good = dispalys <h1>test
Is all because of its HTML Entity References.
It isn't possible for XSS on the title of the window because the title can't render HTML.
SafeHtml.asString() will get you a string, though not the original from SafeHtmlUtils.fromStrong - instead, since the window title can't display any HTML, there is no need to use SafeHtml at all.

GWT: On adding custom widget to celltable losing events of the custom widgets

Our requirement is to make an editable grid using the CellTable containing custom widgets in its cell. The custom widget is having text box and search button associated with the text box. To add the custom widget as a cell created a subclass of AbstractEditableCell class (provided by GWT) and had override render() and onBrowserEvent() methods.
The render(Context context, String value, SafeHtmlBuilder sb) method of the custom widget cell creates a Safe html for the widget and render this safe html in to the cell. But the problem i am facing is, the custom widget is rendered correctly but it loses its associates events. The render method in given below :
if (viewData.isEditing()) {
textBoxSelector.setText(text);
OnlyToBeUsedInGeneratedCodeStringBlessedAsSafeHtml safeHtmlObj = new OnlyToBeUsedInGeneratedCodeStringBlessedAsSafeHtml(textBoxSelector.toString());
sb.append(safeHtmlObj);
} else {
// The user pressed enter, but view data still exists.
sb.append(html);
}
If I try to add the widget in the render() method using the following code, it does not add the widget.
int left = parent.getAbsoluteLeft();
int top = parent.getAbsoluteTop();
String elementId = "ID" + left + top;
try {
parent.setId(elementId);
// parent.removeFromParent();
RootPanel.get(elementId).add(textBoxSelector);
} catch (AssertionError error) {
RootPanel.get(elementId).add(textBoxSelector);
}
I'd really appreciate if anyone can guide me in achieving addition of widget in the CellTable without it losing associated events.
GWT's Cells are non-compatible with GWT Widgets. This means that you could not have a GWT widget placed inside of a cell and have it still function. Cells do have an alternative event handling mechanism though (covered below)
The reason for this is that Cells are closer to stateless renderers. Given a data object the Cell will spit out HTML. A single Cell will be reused over and over - spitting out HTML for various elements on the page - and never maintaining references to any of the DOM elements it creates.
In your above example, you call "someWidget.toString()". Doing this will only return the HTML representation of your widget and is what is loses your event handling.
Handling Events in Cells
GWT Dev Guide for Custom Cells (has some additional details)
To handle events in cells, you'll need to override a separate method called onBrowserEvent. You'll also need to configure your cell to be notified of particular events by calling super('click', 'keydown') in your constructor with the list of events you are interested in listening to.
Since Cells are stateless renderers, the onBrowserEvent will be passed a context of the rendered element that was clicked on along with the original data object that your cell rendered. You can then apply changes or manipulate the DOM as needed.
Here is an example taken from the linked dev guide above:
#Override
public void onBrowserEvent(
Context context,
Element parent,
String value,
NativeEvent event,
ValueUpdater<String> valueUpdater) {
// Let AbstractCell handle the keydown event.
super.onBrowserEvent(context, parent, value, event, valueUpdater);
// Handle the click event.
if ("click".equals(event.getType())) {
// Ignore clicks that occur outside of the outermost element.
EventTarget eventTarget = event.getEventTarget();
if (parent.getFirstChildElement().isOrHasChild(Element.as(eventTarget))) {
doAction(value, valueUpdater);
}
}
}