Search code examples
javascriptjavagwtgwt2gwt-jsinterop

How to expose JS patch function of Incremental DOM library in GWT app using @JsInterop


I would like to use Incremental DOM library in my GWT app.

https://google.github.io/incremental-dom/#about

As I am coming from the Java world, I struggle with concepts of JavaScript namespaces and modules. I was able to use Closure Compiler with closure version of Incremental DOM (has to be build from sources).

It starts with the following line:

goog.module('incrementaldom');

So if I was to use it in regular JS I would type:

var patch = goog.require('incrementaldom').patch;

And then the patch function would be available in the scope of my code. But how to make it accessible from @JsInterop annotated classes?

I tried something like:

public class IncrementalDom {

  @JsMethod(namespace = "incrementaldom", name = "patch")
  public static native void patch(Element element, Patcher patcher);

  @JsFunction
  @FunctionalInterface
  public interface Patcher {
    void apply();
  }

}

But it doesn't work. I get this error in the runtime:

(TypeError) : Cannot read property 'patch' of undefined

So I guess I have to somehow expose the incrementaldom module or at least only the patch method. But I don't know how.


Solution

  • After fighting for the whole day I found the solution. In the goog.module: an ES6 module like alternative to goog.provide document I found the missing information about the role of goog.scope function - required modules are visible only within the scoped call.

    I created another Closure JS file named incrementaldom.js:

    goog.provide('app.incrementaldom'); // assures creation of namespace
    goog.require("incrementaldom");
    
    goog.scope(function() {
      var module = goog.module.get("incrementaldom");
      var ns = app.incrementaldom;
      app.incrementaldom.patch = module.patch;
    });
    
    goog.exportSymbol("app.incrementaldom", app.incrementaldom);
    

    And now I can call it from Java code like this:

    public class IncrementalDom {
    
      @JsMethod(namespace = "app.incrementaldom", name = "patch")
      public static native void patch(Element element, Patcher patcher);
    
      @JsFunction
      @FunctionalInterface
      public interface Patcher {
        void apply();
      }
    
    }
    

    Still I have to define every object exported in original module separately in the Closure JS file. Fortunately I only need patch method. I hope one day I will find less cumbersome way for @JsInterop with goog.module :(