About me

Quick Reminder

Everything I discuss in this presentation are not finalized and has a lot of moving parts...

History 101

Widgets allow you to create high-level components out of DOM elements and tie them together in a widget hierarchy. This is very difficult to do in a coherent way with DOM elements, because you cannot create new DOM element classes.


(from internal GWT docs)

History 102

Today

  • Web is moving with a faster pace

  • No IE6/7/8 - the era of ever-green browsers

    • Standards-based
    • Auto-updated
    • Powerful
  • Web components!

Web Components?

<button is="mega-button">Mega button</button>


W3C Web Components

  • Shadow DOM - mortar/glue
    • DOM & style encapsulation boundaries
  • HTML Templates - scaffold/blueprint
    • inert chunks of clonable DOM. Can be activated for later use (e.g. MDV)
  • Custom Elements - toolbelt
    • create new HTML elements - expand HTML's existing vocabulary
    • extend existing DOM objects with new imperative APIs
  • HTML Imports

See Eric Bidelman's Google I/O 2013 talk

Take away: Widgets for everyone!

Nextgen JSInterop

A modern GWT / Javascript Interoperability layer

Why focus on JS Interop?

  • Web Components and all other HTML5 beauties

  • Much more complex hybrid apps built with GWT and JS together

  • Keeping up with the pace and not re-inventing the wheel

History 201

How do we interoperate with JS?

  • Native functions (JSNI)
  • Javascript object (JSOs)

JSNI

Inline assembly language for GWT

// This is a regular java class
public class Window {
   // Provide other window related functionalities
   ...

   // Shows an alert window
   public static native alert(String msg) /*-{
     // Your regular javascript code goes here
     $wnd.alert(msg);
   }-*/;
}

JSNI - in practice

// Taken from GWT-SDK
protected native void initEventSystem() /*-{
  @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedEvent = $entry(function(evt) {
     if (!@com.google.gwt.user.client.DOM::previewEvent(Lcom/google/gwt/user/client/Event;)(evt)) {
       evt.stopPropagation();
       evt.preventDefault();
       return false;
     }
     return true;
   });

   @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent = $entry(function(evt) {
     var listener, curElem = this;
     while (curElem && !(listener = curElem.__listener)) {
       curElem = curElem.parentNode;
     }
     if (curElem && curElem.nodeType != 1) {
       curElem = null;
     }
     if (listener) {
       if (@com.google.gwt.user.client.impl.DOMImpl::isMyListener(Ljava/lang/Object;)(listener)) {
         @com.google.gwt.user.client.DOM::dispatchEvent(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/EventListener;)(evt, curElem, listener);
       }
     }
   });

   // Some drag events must call preventDefault to prevent native text selection.
   @com.google.gwt.user.client.impl.DOMImplStandard::dispatchDragEvent = $entry(function(evt) {
     evt.preventDefault();
     @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent.call(this, evt);
   });
}-*/;

JavaScriptObject (JSOs)

Opaque representation of native JavaScript objects in GWT

// A JSO
public class Window extends JavaScriptObject {
  // Provide other window related functionalities
  ...

  // Shows an alert window
  public final native alert(String msg) /*-{
    // Your regular javascript code goes here
    this.alert(msg);
  }-*/;
}

Limitation of JSOs

  • Can't be constructed with "new"

  • All public member methods needs to be final

  • Only one single JSO may implement a specific interface

What you can't do in GWT

  • You can’t mix and match type hierarchies from two domains (ie. JS and GWT)

  • You can’t even extend JS object

  • Harder to test with regular junit tests

We can do better

@JsInterface

Defines the contract for javascript object

@JsInteface
interface Window {
  void alert(String msg);
  boolean confirm(String msg);
}

@JsInterface vs. JSO+JSNI

class Window extends JavaScriptObject {
  public native void alert(String msg)/-*{
    this.alert(msg);
  }-*/;
  public native boolean void confirm(String msg)/-*{
    this.confirm(msg);
  }-*/;
  public native boolean addEventListener(String event, EventListener listener)/-*{
    var callback = function(evt) {
	  listener.@com.google.gwt.user.client.EventListener::onBrowserEvent(*)(evt);
    };
    this.addEventListener(event, callback);
  }-*/;
}

vs.

@JsInteface
interface Window {
  void alert(String msg);
  boolean confirm(String msg);
  void addEventListener(String event, EventListener listener);
}

@JsProperty

For accessing properties

@JsInteface
interface Window {
  void alert(String msg);
  boolean confirm(String msg);
  @JsProperty String getName();
  @JsProperty void setName(String name);
}

@JsInterface(prototype="...")

Adding prototype to the contract

@JsInteface(prototype="Window")
interface Window {
  void alert(String msg);
  boolean confirm(String msg);
  @JsProperty String getName();
  @JsProperty void setName(String name);
}

Extending a javascript object

Compiler generates a magic .prototype class

class MyJavaElement extends HTMLElement.protoype {
  // Prototype "Window" is added to the prototype chain of the object
}

@JsExport

Compiler exports @JsExport functions to top scope

class MyJavaElement extends HTMLElement.protoype 
   implements SomeJsInterface {
  @JsExport
  public MyJavaElement() {...}
  @Override // Declared in a @JsInterface so no obfuscation 
  public boolean doSomethingFancy(String x) {...}
}

    Javascript side:

    var element = new MyJavaElement();
    element.doSomethingFancy("x");

And many others...

  • Java 8 defender (a.k.a default) methods for polyfilling

  • GWT.jsni("...") for JSNI without native methods

  • @JsConvert for type conversion

  • @JsAware, @JsWrapper, JsObject, JsArray etc.


For more information:
See Nextgen GWT/Javascript Interop spec

Widget 2.0

Defining the new widget system

Widget 2.0 => Web Components

Simplistic web component support

The imperative approach:

// 1. Define
class MyButton extends HTMLButtonElement.prototype
    implements ElementLifeCycle.Created {
  public void onCreate() {
    setTextContent("My button text");
  }
}

// 2. Register
document.register("my-button", new MyButton());

// 3. Use
root.appendChild(new MyButton());
// or
root.appendChild(document.createElement("my-button"));

Template support

<!-- File: Clock.ui.xml -->		
<template stylesheet="clock.css">
  <div id="clock">
    <div id="second"/> <div id="minute"/> <div id="hour"/>
  </div>
  <div id="time"/>
</template>
@View
class ClockView extends HTMLElement {
  HMTLDivElement time;
  HMTLDivElement second;
  ...
  void onEnteredView() {
    Scheduler.get().scheduleFixedDelay(this::updateTime, 1000);
  }
  private boolean updateTime() {
    Date now = new Date();
    time.setTextContent(DateTimeFormat.getFormat("h:mm a").format(now));
    second.setStyle("transform: rotate(" + calcualteRotateSecond(now) + "deg) ....");
    .... 
  }
}

Template support - short version

@View(template = "<div id=text />" stylesheet="my-button.css")
class MyButton extends HTMLButtonElement {
  DivElement text;

  @UIHandler
  boolean onClick() {
     .... 
  }
}

Template support - Data binding

<template stylesheet="clock.css">
  <div id="clock">
    <div id="second" style="transform: rotate({{rotateSecond}}deg) ..." />
    <div id="minute" style="transform: rotate({{rotateMinute}}deg); ..." />
    <div id="hour" style="transform: rotate({{rotateHour}}deg) ..." />
  </div>
  <div id="time">{{ time }}</div>
</template>
@ViewModel
class ClockViewModel {
  String time;
  int rotateSecond;
  ...
  void onEnteredView() {
    Scheduler.get().scheduleFixedDelay(this::updateTime, 1000);
  }
  private boolean updateTime() {
     Date now =  new Date(); 
     time = DateTimeFormat.getFormat("h:mm a").format(now);
     rotateSecond = calcualteRotateSecond(now);
     ...
  }
}

Next steps

  • Revive GWT Elemental with @JsInterface
    • Type safe abstraction of HTML5 out of the box
  • Build the nextgen templating
    • Reduce the boiler-plate
  • Integrate with the old GWT Widget set
    • Reasonable migration path


Stay tuned!

<Thank You!>