I have a GWT CellTable that gets populated using somewhat of a complicated and tedious process. I want the user to be able to print or export the data from that table.
I would rather not re-render the table contents for export since it is a tedious process.
How can I get the contents of all the rows from all the pages of my CellTable so I can put together a document for printing or export?
I'd be fine with a method of grabbing the actual HTML of the table, or an algorithm for iterating through and grabbing the rendered contents from cells. If someone has a better suggestion, that'd be appreciated as well.
It seems there is no viable way to get the CellTable to give me the data for export without re-rendering contents. Since that would cost the same execution time as doing it myself, I resorted to rendering it myself. I used the following code to render HTML and display it in a new popup for printing. The print() method gets called from my Print button.
/**
* Print in a new popup.
* http://www.coderanch.com/t/564198/GWT/GWT-injecting-HTML-text-browser
*/
public static native void printHTMLString(String htmlString)/*-{
var win = $wnd.open("_blank", "Print", "");
win.document.open("text/html", "replace");
win.document.write("<html><head></head><body>" + htmlString + "</body></html>");
win.document.close();
win.focus();
var headID = win.document.getElementsByTagName("head")[0];
var fileref = win.document.createElement("link");
fileref.setAttribute("rel", "stylesheet");
fileref.setAttribute("type", "text/css");
fileref.setAttribute("href", "tables-min.css");
headID.appendChild(fileref);
win.print();
}-*/;
private void print() {
//get the list from the ColumnSortHandler, so it keeps the sorting on the screen
if (columnSortHandler.getList() == null || columnSortHandler.getList().isEmpty()) {
Window.alert("Nothing to print");
return;
}
SafeHtmlBuilder b = new SafeHtmlBuilder();
b.appendHtmlConstant("<table class=\"pure-table\">"
+ "<thead><tr><th>Timestamp</th><th>Type</th><th>User</th>"
+ "<th>Screen</th><th>Client</th></tr></thead>");
int count = 1;
for (Record r : columnSortHandler.getList()) {
b.appendHtmlConstant("<tr" + (count%2==0 ? ">" : " class='pure-table-odd'>"));
b.appendHtmlConstant("<td>");
b.appendEscaped(timestampFormat.format(timeStampColumn.getValue(r)));
b.appendHtmlConstant("</td>");
b.appendHtmlConstant("<td>");
b.appendEscaped(typeColumn.getValue(r));
b.appendHtmlConstant("</td>");
b.appendHtmlConstant("<td>");
b.appendEscaped(userColumn.getValue(r));
b.appendHtmlConstant("</td>");
b.appendHtmlConstant("<td>");
b.appendEscaped(screenColumn.getValue(r));
b.appendHtmlConstant("</td>");
b.appendHtmlConstant("<td>");
b.appendEscaped(clientColumn.getValue(r));
b.appendHtmlConstant("</td>");
b.appendHtmlConstant("</tr>");
count++;
}
b.appendHtmlConstant("</table>");
printHTMLString(b.toSafeHtml().asString());
}
To export XLS use the class below.
To print use gwt-print-it.
The class below do that without server side.
public class TableToExcel {
public static final <T> void save(final CellTable<T> table, String filename) {
final AnchorElement a = Document.get().createAnchorElement();
a.setHref("data:application/vnd.ms-excel;base64," + base64(table.getElement().getString()));
a.setPropertyString("download", (filename.endsWith(".xls") || filename.endsWith(".xlsx")) ? filename : filename + ".xls");
Document.get().getBody().appendChild(a);
Scheduler.get().scheduleEntry(new ScheduledCommand() {
#Override
public void execute() {
click(a);
a.removeFromParent();
}
});
}
private static native void click(Element elem) /*-{
elem.click();
}-*/;
public static native String base64(String data) /*-{
return btoa(data);
}-*/;
}
You can use getInnerHTML or getInnerText
for(int i=0; i<cellTable.getRowCount();i++)
{
TableRowElement rowElement = cellTable.getRowElement(i);
for(int j =0; j<rowElement.getCells().getLength(); j++)
{
//System.out.println( rowElement.getCells().getItem(j).getInnerHTML() );
System.out.println( rowElement.getCells().getItem(j).getInnerText() );
}
}
You could try the gwt-table-to-excel module.
The main point is that excel and other modern spreadsheet knows how to render an html table, so just open the dynamically built html table with the correct mime-type, and that's all.
I never used it, but the description sounds good.
Related
I am trying to construct a DataGrid in GWT that will show an arbitrary dataset taken from an rpc method.
I have done some progress as I get the fields from a method and the data from another.
I have managed to construct the Datagrid and add the columns from the rpc.getFields() method and fill the table using an AsyncDataProvider.
The problem is that when I refresh the browser, it duplicates all the columns at the Datagrid. I cannot figure out what to do. I tried to remove first all the columns but no luck.
I attach the code if anyone have an idea.
public class MyCallBack implements AsyncCallback<List<Field>> {
DataGrid<Record> dg;
public MyCallBack(DataGrid<Record> dgrid) {
this.dg=dgrid;
}
public void onFailure(Throwable caught) {
Window.alert(caught.getMessage());
}
public void onSuccess(List<Field> result) {
for (int i=0;i<=result.size();i++) {
IndexedColumn ic = new IndexedColumn(i);
dg.addColumn(ic, result.get(i).getLabel());
}
}
public AsyncCallback<List<Field>> getCb() {
return this;
}
public void onModuleLoad() {
final DataGrid<Record> dg = new DataGrid<Record>();
MyCallBack mcb = new MyCallBack(dg);
DataProvider dp = new DataProvider();
DBConnectionAsync rpcService = (DBConnectionAsync) GWT.create(DBConnection.class);
ServiceDefTarget target = (ServiceDefTarget) rpcService;
String moduleRelativeURL = GWT.getModuleBaseURL() + "MySQLConnection";
target.setServiceEntryPoint(moduleRelativeURL);
rpcService.getFields(mcb.getCb());
dp.addDataDisplay(dg);
dg.setVisibleRange(0, 200);
SplitLayoutPanel slp = new SplitLayoutPanel();
slp.setHeight("700px");
slp.setWidth("1500px");
slp.addWest(dg, 770);
RootPanel.get().add(slp);
}
When you refresh a browser, all UI is lost. There is no difference between (a) show the UI for the first time or (b) show the UI after browser refresh.
Your comment "Only if I restart tomcat it works" suggests that the problem is on the server side. Most likely, you return twice the number of data points on a second call.
Try clearing the table before filling it like this:
public void onSuccess(List<Field> result) {
clearTable();
for (int i=0;i<=result.size();i++) {
IndexedColumn ic = new IndexedColumn(i);
dg.addColumn(ic, result.get(i).getLabel());
}
}
private void clearTable(){
while (dg.getColumnCount() > 0) {
db.removeColumn(0);
}
}
I have working on CellList for weeks now and I find it owesome.
Those days I wish to print a list that contains more than 70 elements but I noticed that my CellList doesn't append more than 25: from 0 to 24. To make sure that the problem has no relashionsip with my project, I decided to test the code in a new and clan project but It has the same result.
Here is my code:
CustomCell bodyCell = new CustomCell();
CellList<String> coreCellList = new CellList<String>(bodyCell);
List<String> list = new ArrayList<String>();
for (int i=0; i<74; i++) {
list.add("hello_"+i);
}
coreCellList.setRowData(0, list);
coreCellList.setRowCount(list.size(), true);
RootPanel.get().add(coreCellList);
and CustomCell:
public class CustomCell extends AbstractCell<String> {
interface Templates extends SafeHtmlTemplates {
String style = "cell";
#SafeHtmlTemplates.Template("<div class=\"" + style
+ "\" style = 'height : 15%'>{0}<br/></div>")
SafeHtml cell(SafeHtml value);
}
private static Templates templates = GWT.create(Templates.class);
#Override
public void render(com.google.gwt.cell.client.Cell.Context context,
String value, SafeHtmlBuilder sb) {
SafeHtml safeValue = SafeHtmlUtils.fromString(value);
SafeHtml rendered = templates.cell(safeValue);
sb.append(rendered);
}
}
Thank you for your help.
I always use the code below passing my CellTables and CellLists when I don't want pagination.
public static void setupOnePageList(final AbstractHasData<?> cellTable) {
cellTable.addRowCountChangeHandler(new RowCountChangeEvent.Handler() {
#Override
public void onRowCountChange(RowCountChangeEvent event) {
cellTable.setVisibleRange(new Range(0, event.getNewRowCount()));
}
});
}
CellList is using paging by default, having a page size of 25. If you add a SimplePager to your page, you can control which data you want to display.
I would advise not to disable paging by setting the page size to the size of your list of data, as you will run into serious performance issues with older browsers (especially IE8 and earlier), depending on the complexity of your presentation and size of the list.
For adding the Pager, see the DevGuide:
// Create a SimplePager.
SimplePager pager = new SimplePager();
// Set the cellList as the display.
pager.setDisplay(cellList);
I'm pretty new to GWT, but I've been making pretty fast progress until now.
I have a cell table, most of which is read only data returned from an RPC.
I have two columns in the cell table that the user can interact with. One is a TextInputCell, one is a ButtonCell.
When the user clicks the ButtonCell, i want to send the value in the TextInputCell for that row to an RPC.
I have all this working.
The part I cannot get to work is that when the button (ButtonCell) is clicked, I want to disable the button in that row until the RPC returns, and then re-enable it. I also want to clear the text in the input cell for that row when the RPC returns.
I cannot figure out how to get handles to the actual ButtonCell object that was clicked or the TextInputCell to monkey with them.
Any help appreciated.
bq
The problem is that there's no object for the button that was clicked. Your ButtonCell creates HTML that renders buttons - every button in the whole column was written by the same button cell, but there's no java object associated with them.
To disable the button directly, you'll have to first create a handle to it. You could do this by rendering an id in the html your ButtonCell creates, and then getting the element by id from the DOM.
What I do in a similar case is just re-render the entire table when there's a state change. It doesn't take that long, and you don't need to store any references (the whole reason you're using CellTable instead of Grid anyway). When you know your button should be disabled, you just render it disabled.
Both of these suggestions would require you to subclass your Cell objects so that you can do some custom rendering. It's not very difficult, but wrapping your head around the order of operations can be confusing. Good luck!
PS: If you just want to disable the button (and not empty the text field), I think onBrowserEvent gives you a handle to the Element that was clicked - you might be able to use that to disable it.
I have gone through this problem, but eventually I solved it.
check this code
package com.ex7.client;
import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
public class CWButton extends ButtonCell {
private int row = -1;
private String alternativevalue;
private String exTitle = "";
private String value;
private String title = "";
public CWButton( ) {
super();
}
#Override
public void render(com.google.gwt.cell.client.Cell.Context context,
String src, SafeHtmlBuilder sb) {
if (row == -1) {
sb.appendHtmlConstant("<button title='" + title + "' >" +value+"</button>");
return;
}
if (row != context.getIndex()) {
sb.appendHtmlConstant("<Button disabled='disabled' title='" + title + "' >"+ value+"</button>");
} else {
sb.appendHtmlConstant("<button title='" + exTitle + "' >"+ alternativevalue+"</button>");
}
}
#Override
public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context,
Element parent, String value, NativeEvent event,
ValueUpdater<String> valueUpdater) {
if (row == -1 || row == context.getIndex()) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
return;
}
}
public void setTitle(String title) {
this.title = title;
}
public int getRow() {
return row;
}
public String getExTitle() {
return exTitle;
}
public void setExTitle(String exTitle) {
this.exTitle = exTitle;
}
public void setRow(int row) {
this.row = row;
}
public String getAlternativeValue() {
return alternativevalue;
}
public void setAlternativeValue(String alternativeValue) {
this.alternativevalue = alternativeValue;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
EDIT: This seems to be a bug.
I'm trying to make CellTable work together with AsyncListViewAdapter<T> and SimplePager<T>. The data gets displayed, but when the pager should be 'deaf' (meaning when all existing data are displayed) it still receives clicks and, more importantly, makes the displayed data go away. Instead of my data 'loading' indicator gets displayed, and it keep loading and loading... Obviously nothing gets loaded, as it doesn't even call the onRangeChanged handler.
I went through the code-snippets in this thread, but I can't see anything suspicions on what I've been doing.
Is there some obvious answer to a rookie mistake?
I shrinked my variable names, hopefully it won't wrap too much.
protected class MyAsyncAdapter
extends AsyncListViewAdapter<DTO> {
#Override
protected void onRangeChanged(ListView<DTO> v) {
/*
* doesn't even get called on [go2start/go2end] click :(
*/
Range r = v.getRange();
fetchData(r.getStart(), r.getLength());
}
}
private void addTable() {
// table:
CellTable<DTO> table = new CellTable<DTO>(10);
table.addColumn(new Column<DTO, String>(new TextCell()) {
#Override
public String getValue(DTO myDto) {
return myDto.getName();
}
}, "Name");
// pager:
SimplePager<DTO> pager = new SimplePager<DTO>(table);
table.setPager(pager);
adapter = new MyAsyncAdapter();
adapter.addView(table);
// does not make any difference:
// adapter.updateDataSize(0, false);
// adapter.updateDataSize(10, true);
VerticalPanel vPanel = new VerticalPanel();
vPanel.add(table);
vPanel.add(pager);
RootLayoutPanel.get().add(vPanel);
}
// success-handler of my fetching AsyncCallback
#Override
public void onSuccess(List<DTO> data) {
// AsyncCallback<List<DTO>> has start field
adapter.updateViewData(start, data.size(), data);
if(data.size() < length)
adapter.updateDataSize(start + data.size(), true);
}
Regards
J. Záruba
Apparently because of a bug.
Can anyone tell me how to print gwt widget?
I gont through the folowing thread and tried .
http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/eea48bafbe8eed63
Still I cant do that.I can able to print the widget without any css style.I want to include the css styles(css files) also.Thanks for your valuable help.
I have faced similar situation using the same link you have used.
It prints String, Widgets,Or Elements without any problem.
Solution I found is that you need some delay before you call Print.
So, There are few alternate solutions.
1. Write down a loop which causes delay of 2-3 seconds.
2. Use Time to get delay of 2-3 seconds
3. Use Window.Confirm to ask some question like 'Do you want to print ?'
Hope this will help to others
You can create your own class and on click of Print button in your application, by using native print method you can call Window.Print().
GWT UIObject can be used for this purpose and the method and it will be converted into string.
public class NTPrint {
/**
* If true, use a Timer instead to print the internal frame
*/
public static boolean USE_TIMER = true;
/**
* Time in seconds to wait before printing the internal frame when using Timer
*/
public static int TIMER_DELAY = 1;
public static native void it() /*-{
$wnd.print();
}-*/;
public static void it(String html) {
try{
buildFrame(html);
if (USE_TIMER) {
Timer timer = new Timer() {
public void run() {
printFrame();
}
};
timer.schedule(TIMER_DELAY * 1000);
} else {
printFrame();
}
}
catch (Throwable exc) {
CommonUtil.printStackTrace(exc);
Window.alert(exc.getMessage());
}
}
/**
* This method will be called when you pass Widget without style.
* #param uiObjects
*/
public static void it( UIObject...uiObjects) {
StringBuffer objString= new StringBuffer();
for(UIObject obj: uiObjects)
objString.append(obj.toString());
it("", objString.toString());
}
}
OnClick of Print button,
/**
* prints all forms
* #param documentInfo
*/
public static void printForms(List<FormTransaction> formTransactions, String documentInfo, List<CellTransaction> cellTransactionList, boolean isComment, Document document) {
String style = "<link rel='styleSheet' type='text/css' href='v4workflow/css/style.css'>"
+ "<link rel='styleSheet' type='text/css' href='v4workflow/css/gwtcontrols.css'>"
+ "<style type='text/css' media='print'>"
+ "#media print {"
+ ".footerText{font-size:13px; font-weight:normal;margin-top:0px;}"
+ ".break {page-break-after:always}"
+ "}" + "</style>";
List<UIObject> uiObjects = new ArrayList<UIObject>();
NTPrintLayout printLayout = new NTPrintLayout();
printLayout.setSelectedDocument(null);
uiObjects.add(createHeader());
NTPrint.it(style,uiObjects);
}
public static FlexTable createHeader(){
FlexTable flexTable = new FlexTable();
flexTable.setWidth("100%");
return flexTable;
}