Reputation: 294
I want to be able to call a method declared in my StartView
class from the onclick
event in Javascript. I am following the official GWT documentation, but am still getting a JavaScriptException TypeError - doThing is not a function
error in the console. I have reviewed other similar questions here on SO and have not found a solution as of yet. I have attempted to implement a solution in JSNI and JSInterop - and what I have below is the furthest I have gotten past GWT compilation errors.
StartView.java - I call StartView.loadJS()
when StartView
is initialized
public void doThing() {
System.out.println("do thing");
}
public static native void loadJS() /*-{
var that = this;
$wnd.doThing = $entry(function() {
[email protected]::doThing()();
});
}-*/;
start.js (External js file, Injected into StartView via ScriptInjector. Other methods use JSInterop)
...
// Inside of my onclick handler
window.doThing();
When I click the element, I get the following error in the console:
Exception: com.google.gwt.core.client.JavaScriptException: (TypeError) : that_0_g$.doThing_0_g$ is not a function
When I put window.doThing
in the console, it does return the function - so it does appear to be loading the connector, but is having trouble finding the actual method to call. Curious what else I am missing. Would appreciate any advice.
Upvotes: 0
Views: 240
Reputation: 18331
First, obligatory "Don't use JSNI for new code" - unless you project relies on an ancient GWT build, this is easier in JsInterop. First, define (or reuse) a @JsFunction
interface that describes the contract for JS to call into Java, make a global field that you can assign this to, and then export your instance method in vanilla java:
/** Like Runnable, but no default methods, and has {@link JsFunction}. */
@JsFunction
public interface JsRunnable {
void run(); // your example has no params and no return type, so this fits
}
public static void loadJS() {
JsRunnable lambda = this::doThing; // bug is clear in java!
// Assign as a new property on window
Js.asPropertyMap(DomGlobal.window).set("doThing", lambda);
}
public void doThing() {
System.out.println("do thing");
}
Note that the translation of loadJS makes it clear why your JSNI isn't working (more on that in a moment) - Java will complain if you try to use this
, since loadJS()
is static and has no this
! But, you're calling loadJS()
from inside the constructor, you could pass the instance as an argument, or make loadJS()
non-static.
From your question
public static native void loadJS() /*-{
var that = this; // Where should "this" come from?
$wnd.doThing = $entry(function() {
[email protected]::doThing()();
});
}-*/;
As this is a static
method, there is no this
(okay, in JS there is a this
, but it isn't the StartView
, it is something else entirely). Since doThing
is a non-static
method, there is no clear way to say which StartView
instance you want to have doThing()
called on.
The simplest answer then is to remove static
from that method, so that this
is correctly captured, as you intended. Another choice could be to pass in a StartView
instance to loadJS
and call doThing on that instance.
The error you're getting is almost certainly that this
turns out to be window
, and the @package.StartView::doThing()
reference represents some obfuscated method name that doesn't exist on window, which makes sense, since you intended this
to be a StartView
instance.
Upvotes: 2