JSNI - interactive chart

102 views
Skip to first unread message

George Agiasoglou

unread,
Oct 13, 2011, 10:15:28 AM10/13/11
to google-we...@googlegroups.com
Hi there, 

I have a chart which lives in a google spreadsheet and has been published as an interactive chart.

Is it possible to render the following chart in an HTML element for instance in GWT, without any sort of parsing or processing?

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js"> {"dataSourceUrl":"//location.to.document","options":{"vAxes":[{"min":null,"title":"Hits","max":null}],"cat":false,"title":"someMetric","titleX":"Date","backgroundColor":"#FFFFFF","legend":"right","logScale":false,"reverseAxis":false,"hAxis":{"maxAlternation":1},"hasLabelsColumn":true,"isStacked":false,"width":600,"height":371},"state":{},"chartType":"ColumnChart","chartName":"Chart 1"}</script>


Thanks,

-G

Tomasz Gawel

unread,
Oct 13, 2011, 11:33:47 AM10/13/11
to Google Web Toolkit
chart.js script uses document.write() to write chart markup. in case
you put this dynamically in your HTML Widget's it would reload the
page (document.write behaviour).
the only possibility to create it on runtime in gwt is to put it into
generated iframe.

Tomasz Gawel

unread,
Oct 13, 2011, 12:10:02 PM10/13/11
to Google Web Toolkit
public class TestChart implements EntryPoint {

static class YourChart extends Widget {
private static final String HTML_BEGIN = "<html><head></
head><body><script type=\"text/javascript\" src=\"http://
ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js\">";
private static final String HTML_END = "</script></body></html>";

public YourChart(JSONObject userConf) {
this(userConf.toString());
}
public YourChart(String userConf) {
IFrameElement elem = Document.get().createIFrameElement();
elem.setInnerHTML(HTML_BEGIN + userConf + HTML_END);
setElement(elem);
}

}

@Override
public void onModuleLoad() {
JSONObject conf = new JSONObject(getConf());
RootPanel.get().add(new YourChart(conf));
}

private static native JavaScriptObject getConf() /*-{
return {
};
}-*/;

}

George Agiasoglou

unread,
Oct 14, 2011, 11:02:28 AM10/14/11
to google-we...@googlegroups.com
Hi Tomasz,

Many thanks for your sample code. The only thing missing is inherit Json in your application module   <inherits name="com.google.gwt.json.JSON" />

Although it compiles and runs the iframe comes out empty.

I stepped through the code in debug mode and the element created is shown below:

<iframe><html><head></head><body><script type="text/javascript" src="http://ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js">{"dataSourceUrl":"location.to.document", "options":{"vAxes":[{"min":null, "title":"Hits", "max":null}], "cat":false, "title":"metrics", "titleX":"Date", "backgroundColor":"#FFFFFF", "legend":"right", "logScale":false, "reverseAxis":false, "hAxis":{"maxAlternation":1}, "hasLabelsColumn":true, "isStacked":false, "width":600, "height":371}, "state":{}, "chartType":"ColumnChart", "chartName":"Chart 1"}</script></body></html></iframe>

 Inspecting the element in firebug I get the following:

<iframe>
  <html>
    <head></head>
    <body></body>
  </html>
</iframe>

Any idead what might be going wrong?

Thanks,
-G

Tomasz Gawel

unread,
Oct 15, 2011, 1:16:27 PM10/15/11
to Google Web Toolkit
it seems that it is not possible to write iframe's content directly in
html.
you can add it programatically which seems even more convenient (but i
wanted to avoid it do to some inconsistency between browser as to when
cotnentDocument of an iframe becames available to code - there can
happen some delay in some browser and onload event is called event if
this iframe does not load any remote content.)
so with this precaution here's the code:


static class YourChart extends Widget {

private static final String API_URL = "http://ajax.googleapis.com/
ajax/static/modules/gviz/1.0/chart.js";
private HandlerRegistration handlerRegistration;

public YourChart(JSONObject userConf) {
this(userConf.toString());
}

public YourChart(final String userConf) {
final IFrameElement elem =
Document.get().createIFrameElement();

setElement(elem);

if(elem.getContentDocument() == null ||
elem.getContentDocument().getBody() == null){

handlerRegistration = addDomHandler(new LoadHandler(){

@Override
public void onLoad(LoadEvent event) {
initFrame(elem, userConf);
}

}, LoadEvent.getType());
}
else {
initFrame(elem, userConf);
}
}

private void initFrame(IFrameElement elem, String userConf) {
if(handlerRegistration != null){
handlerRegistration.removeHandler();
handlerRegistration = null;
}
Document cDoc = elem.getContentDocument();
BodyElement body = cDoc.getBody();
ScriptElement script = cDoc.createScriptElement(API_URL);
script.setInnerText(userConf);
body.appendChild(script);
}
}

George Agiasoglou

unread,
Oct 18, 2011, 9:50:15 AM10/18/11
to google-we...@googlegroups.com
Hi Tomasz,

and thanks again for your guidance.

I had partial success but only after a made few changes. Running the above code throws an exception

com.google.gwt.core.client.JavaScriptException: (TypeError): Cannot read property 'document' of null 

which is probably related to what you mentioned, that the document may not be available (i am using latest version of chrome). 

So I made a few changes, instead of adding addDomHandler... I override the onLoad(), so initFrame is called like this,

@Override
protected void onLoad() {initFrame(userConf);}

And the output is:

<html>
 <head></head>
 <body>
   <script>{userconf....}</sctipt>
 </body>
</html

But still I get not graph.. shoudnt the script tag be added on the head element?

Thanks,
G

Reply all
Reply to author
Forward
0 new messages