简体   繁体   中英

Error when passing Java object through GWT JSNI

I've got a GWT project that uses the Google JavaScript Chart API to show charts of Java objects. I convert my Java objects to JSON and hand them off to the JavaScript chart API through JSNI , and that works perfectly.

But now I'd like to go a step further and go the other way: when I click inside a chart (which I can detect through a JavaScript listener), I want to send that Java object back to my Java code.

I think I've done everything in the JSNI guide, but when I click the chart, I get an error in the JavaScript console that I'm not even sure how to read:

SCRIPT5022: Exception thrown and not caught
File: eval code (21), Line: 5, Column: 7

__gwt_javaInvokes[2] =
  function(thisObj, dispId,p0,p1) {
    var result = __static(dispId, thisObj,p0,p1);
    if (result[0]) {
      throw result[1];
    } else {
      return result[1];
    }
  } function(thisObj, dispId,p0,p1) { var result = __static(dispId, thisObj,p0,p1); if (result[0]) { throw result[1]; } else { return result[1]; } }

Here is my html page:

<!doctype html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <link type="text/css" rel="stylesheet" href="GwtTest.css">
    <title>GWT Test</title>
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    <script type="text/javascript" language="javascript" src="gwttest/gwttest.nocache.js"></script>
  </head>
  <body>

  </body>
</html>

The only thing of interest there is that I've added the Google Chart API script tag.

Here is the Java object I'm trying to pass from Java, to JavaScript, then back to Java:

package com.example.client;

public class ChartData {
    public String title = "test";

    public String labelOne = "thingOne";
    public String labelTwo = "thingTwo";
    public String labelThree = "thingThree";

    public int valueOne = 100;
    public int valueTwo = 200;
    public int valueThree = 300;
}

Finally, here is my GWT client class:

package com.example.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsonUtils;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;

public class GwtTest implements EntryPoint {

    public void onModuleLoad() {

        RootLayoutPanel rootPanel = RootLayoutPanel.get();

        SimplePanel chartPanel = new SimplePanel();
        chartPanel.getElement().setId("chartDiv");
        chartPanel.setSize("500px", "500px");

        Button button = new Button("Click Me");
        button.addClickHandler(new ClickHandler(){

            @Override
            public void onClick(ClickEvent event) {
                ChartData chartData = new ChartData();
                String json = getChartJson(chartData);
                JsArray<?> chartArrayData = JsonUtils.safeEval(json);
                drawChart(chartArrayData, chartData);
            }
        });

        VerticalPanel vp = new VerticalPanel();
        vp.add(button);
        vp.add(chartPanel);

        rootPanel.add(vp);

        loadChartLibrary();
    }

    private String getChartJson(ChartData chartData){
        String json = "[";
        json += "[\"time\", \"" + chartData.title + "\"]";

        json += ",";
        json += "[\"" + chartData.labelOne + "\"," + chartData.valueOne + "]";

        json += ",";
        json += "[\"" + chartData.labelTwo + "\"," + chartData.valueTwo + "]";

        json += ",";
        json += "[\"" + chartData.labelThree + "\"," + chartData.valueThree + "]";

        json += "]";
        return json;
    }

    private native void loadChartLibrary()/*-{
        $wnd.google.charts.load('current', {packages: ['corechart']});
    }-*/;

    private native void drawChart(JsArray<?> dataArray, ChartData chartData)/*-{
        var data = $wnd.google.visualization.arrayToDataTable(dataArray);
        var chart = new $wnd.google.visualization.ColumnChart($wnd.document.getElementById('chartDiv'));
        chart.draw(data, null);

        $wnd.google.visualization.events.addListener(chart, 'select', selectHandler);

        function selectHandler(e) {
          var selectionArray = chart.getSelection();
          if(selectionArray.length == 0){

          }
          else{
            var row = selectionArray[0].row;
            console.log(row);
            this.@com.example.client.GwtTest::printChartData(Lcom/example/client/ChartData;I)(chartData, row);
            console.log("here");
          }
        }

    }-*/;

    public void printChartData(ChartData chartData, int i){
        System.out.println(i + ": " + chartData.valueThree);
    }
}

If you run this code, you should see a button that says Click Me. Click that button to show the chart. Then click a bar in the chart. I would expect this to trigger my Java code in the printChartData() function, but instead I get an error in the JavaScript console.

I've narrowed the problem down to this bit of code:

var row = selectionArray[0].row;
console.log(row);
this.@com.example.client.GwtTest::printChartData(Lcom/example/client/ChartData;I)(chartData, row);
console.log("here");

The row is printed to the console correctly, so I know my JavaScript is working. But then the "here" is never printed to the console, so I know the error is in my JSNI line.

Am I missing something with my JSNI setup? What does the JavaScript error mean?

I think that the problem is in using this inside a Handler. You propably expect this to be instance of GwtTest class but used inside a Handler it points to the handler. And that's why the call to this.@com.example.client.GwtTest::printChartData fails.

You can declare a variable outside the handler and assign this and then use it inside the handler.

Try:

var instance = this;
function selectHandler(e) {
      var selectionArray = chart.getSelection();
      if(selectionArray.length == 0){

      }
      else{
        var row = selectionArray[0].row;
        console.log(row);
        instance.@com.example.client.GwtTest::printChartData(Lcom/example/client/ChartData;I)(chartData, row);
        console.log("here");
      }
    }

ChartData is not extending JavaScriptObject , so it throws a ClassCastException when you call
printChartData .
Also Adam is right. Using this inside the handler does not work...
Here is an example, how it could work, just override your whole GwtTest class..

public class GwtTest implements EntryPoint {

private ChartData chartData;

public void onModuleLoad() {
    RootLayoutPanel rootPanel = RootLayoutPanel.get();
    SimplePanel chartPanel = new SimplePanel();
    chartPanel.getElement().setId("chartDiv");
    chartPanel.setSize("500px", "500px");
    Button button = new Button("Click Me");
    button.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            chartData= ChartData.createJSO("Time", "Time");
            for (int i = 0; i < 4; i++) {
                RowData row = RowData.createJSO("label" + i, 100 + i);
                chartData.add(row);
            }
            drawChart(chartData);
        }
    });
    VerticalPanel vp = new VerticalPanel();
    vp.add(button);
    vp.add(chartPanel);
    rootPanel.add(vp);
    loadChartLibrary();
}

public static class ChartData extends JavaScriptObject {
    protected ChartData() {
    }

    public static native ChartData createJSO(String chartname, String rowTypeDescriton) /*-{
        return [ [ chartname, rowTypeDescriton ] ];
    }-*/;

    public final native RowData get(int index) /*-{
        return this[index + 1];
    }-*/;

    public final native void add(RowData data) /*-{
        this[this.length] = data;
    }-*/;

}

public static class RowData extends JavaScriptObject {

    protected RowData() {
    }

    public static native RowData createJSO(String label, int value) /*-{
        return [ label, value ];
    }-*/;

    public final native String getLabelOne() /*-{
        return this[0];
    }-*/;

    public final native int getValueOne() /*-{
        return this[1];
    }-*/;

}

private native void loadChartLibrary()/*-{
    $wnd.google.charts.load('current', {
        packages : [ 'corechart' ]
    });
}-*/;

private native void drawChart(ChartData dataArray)/*-{
    var self = this;
    var data = $wnd.google.visualization.arrayToDataTable(dataArray);
    var chart = new $wnd.google.visualization.ColumnChart($wnd.document
            .getElementById('chartDiv'));
    chart.draw(data, null);
    $wnd.google.visualization.events.addListener(chart, 'select',
            selectHandler);
    function selectHandler(e) {
        var selection = chart.getSelection();
        for (var i = 0; i < selection.length; i++) {
            var item = selection[i];
            if (item.row != null) {
                self.@com.example.client.GwtTest::printChartData(I)(item.row);
            } 
        }
    }

}-*/;

public void printChartData(int row) {
    System.out.println(chartData.get(row));
}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM