Wie GWT mit seinen Spielkameraden redet

GWT meets Ajax und PHP

Frank Wisniewski

Java Magazin

Der Artikel „GWT meets Ajax und PHP“ von Frank Wisniewski ist erstmalig erschienen im

Java Magazin 10.2012

GWT in eigenes Webprojekt einbetten

Zur Einbindung von GWT-Elementen in den eigenen Webauftritt gibt es mehrere Wege. Diese möchte ich anhand der Beispielapplikation zeigen, welche mit dem Toolkit ausgeliefert wird. Dazu wird ein neues Projekt mit den Einstellungen aus Abbildung 2 angelegt und jegliche Serverlogik entfernt (wir wollen schließlich nachher einen alternativen Server verwenden). Zusätzlich muss die generierte CSS-Datei entfernt und alle Themes in der *.gwt.xml auskommentiert werden. Letzteres würde dazu führen, dass GWT das im Header der HTML-Datei spezifizierte Layout teilweise überschreibt. Dabei spielt es keine Rolle, ob das generierte JavaScript vor oder nach den Style-Sheet-Definitionen aufgerufen wird.

Abb. 2: Einstellungen zum Anlegen der Beispielapplikation in Eclipse
GWT-Komponenten in das Layout einbinden

Als Prototyp für einen bereits existierenden Webauftritt habe ich das Bootstrap-Framework von Twitter ausgewählt (Kasten: „Twitter Bootstrap“). Wir wollen nun die GWT-Beispielapplikation in ein Bootstrap-Formular portieren. Dazu muss die von der Grundschablone generierte Seite durch dessen Template „Basic Marketing Site“ ersetzt werden. Dann fügen wir das Formular mit einem Textfeld in die HTML-Seite ein:


Listing 2 zeigt drei Varianten zur programmatischen Einbindung von Widgets. Eine vierte Möglichkeit zur Instanziierung einer GWT-Komponente über einen JavaScript-Callback zu einem GWT-Konstruktor werde ich später vorstellen.

Twitter Bootstrap

Bootstrap ist ein Toolkit zur Erstellung von Web-Frontends [5]. Es beinhaltet CSS-Templates, die mit JavaScript um bestimmte Effekte wie geräteübergreifende Darstellung erweitert werden. Die vorgefertigten GUI-Komponenten werden dazu mittels CSS Media Queries automatisch an Tablet-, Smartphone- und Desktopclients angepasst.

Twitter benutzt diese Bibliothek zur Darstellung der hauseigenen Webseite und stellt sie unter einer Open-Source-Lizenz der Allgemeinheit zur Verfügung (der Code steht unter der Apache-2.0-Lizenz). Das Grundgerüst bildet ein aus zwölf Spalten bestehendes Grid-System, worüber sich ein relationales Layout definieren lässt. Das Framework versucht dann gemäß dieser Gewichtung eine passende Repräsentationsform für den jeweiligen Bildschirm zu finden.

Ein guter Startpunkt sind die mitgelieferten Beispiele, wovon auch eines in der Demoanwendung zu diesem Artikel verwendet wird. Ein Projekt zur Portierung von Bootstrap auf GWT existiert bereits und ist unter [6] zu finden.

GWT als Wrapper für DOM-Objekte

In den ersten sieben Zeilen des Programmschnipsels wird das input-Objekt über das RootPanel als DOM-Element bezogen. Dieses wird dann direkt über den Konstruktor in die TextBox gewrappt. Anschließend wird noch der Inhalt mit einer kurzen Botschaft als Platzhalter gefüllt. Die Besonderheit dieser Variante ist, dass das DOM-Element im wahrsten Sinne als Konstruktor-Argument dient. D. h. es werden die Attribute und Inhalte des Elements auf das resultierende GWT-Widget übertragen. Ferner lässt sich die HTML-Seite vollständig ohne das GWT layouten und designen. Durch das GWT wird anschließend lediglich interaktive JavaScript-Logik für die gewrappten Elemente generiert.

Listing 2

final TextBox nameField;
if (RootPanel.get("nameFieldContainer") != null) {
  nameField = TextBox.wrap(RootPanel.get("nameFieldContainer").getElement());
  nameField.setText("Java Magazin Reader");
} else {
  nameField = null;
}

final Button sendButton;
if (RootPanel.get("bootstrapForm") != null) {
  sendButton = new Button("Send");
  sendButton.setStyleName("btn btn-success");
  RootPanel.get("bootstrapForm").add(sendButton);
} else {
  sendButton = null;
}

final Label errorLabel;
Element element = Document.get().getElementById("bootstrapForm");
if (element != null) {
  errorLabel = new Label();
  element.appendChild(errorLabel.getElement());
} else {
  errorLabel = null;
}
GWT-Komponenten über RootPanel einbinden

Die zweite Variante gibt im Prinzip die Vorgehensweise der Beispielanwendung zum Einbinden von GWT-Komponenten wieder. Hier wird das form-Element als RootPanel gewrappt und auf diesem in seiner Eigenschaft als Widget über die add()-Methode ein frisch instanziierter Button hinzugefügt. Dadurch wird das Standard-Styling aktiviert. D. h. dass jede UI-Klasse des GWT grundsätzlich einer Style-Sheet-Klasse angehört. Der Name setzt sich aus dem Prefix gwt- gefolgt vom Klassennamen zusammen. Für bestehende Lösungen muss dieser Wert also jeweils über setStyleName() von Hand überschieben werden. Die letzten Zeilen von Listing 2 demonstrieren eine Verfahrensweise, bei der eine Abstraktion der GWT-Widgets auf ihre kennzeichnenden Element-Objekte stattfindet. Anschließend wird das Label über die Methoden des GWT-DOM-API dem Formular angefügt. Die Style-Sheet-Definitionen verhalten sich analog zu oben.

Eigene GWT-Elemente kreieren und einbinden

Im folgenden Beispiel möchte ich zeigen, wie eine externe JavaScript-Bibliothek in die eigene GWT-Anwendung eingebunden werden kann. Hierfür verwende ich „Google Code Prettify“, einen Syntax-Highlighter auf Basis von JavaScript und CSS [7]. Das Skript und Style Sheet werden über das ClientBundle-Interface direkt aus den Sourcen eingebunden und die benötigten Funktionen des Highlighters werden über das JSNI angesprochen.

Die gesamte Logik wird in dem neuen Widget CodePrettifyWidget gebündelt (Listing 3). Im Konstruktor wird definiert, dass ein <pre>-Element verwendet werden soll und dass das class-Attribut auf prettyPrint gesetzt wird. Durch das Auslösen der nativen Methode prettyPrint() bearbeitet das importierte JavaScript das HTML-Element und kreiert die in Abbildung 3 gezeigte Darstellung mit Syntaxhervorhebungen. Die angezeigten Quelltexte werden durch eine JavaScript-Routine geladen, welche wiederum eine exportierte GWT-Methode aufruft.

Listing 3

public class CodePrettifyWidget extends Widget {

  // statische Klassenvariable verhindert mehrmaliges Einfuegen des Scripts
  protected static boolean injected = false;

  public CodePrettifyWidget() {
    inject();  
    setElement(Document.get().createPreElement());
    setStyleName("prettyprint");
  }
  
  protected static void inject() {
    if (!injected) {
      CodePrettifyBundle.INSTANCE.prettifyStylesheet().ensureInjected();

      String prettifyJS = 
          CodePrettifyBundle.INSTANCE.prettifyScript().getText();
      // Window muss gesetzt werden, da GWT Script
      // versehentlich in History-Frame injeziert
      ScriptInjector.fromString(prettifyJS).setWindow(getWindow()).inject();
      
      injected = true;
    }
  }
  
  public static native JavaScriptObject getWindow() /*-{
    return $wnd;
  }-*/;

  private static native void prettyPrint() /*-{
    $wnd.prettyPrint();
  }-*/;

  public static void prettify() {
    // Sicherstellen, dass Skript geladen ist
    inject();
    // JNI-Methode aufrufen
    prettyPrint();
  }

  public void setCode(String code) {
    getElement().setInnerText(code);
  }

public interface CodePrettifyBundle extends ClientBundle {

  public static final CodePrettifyBundle INSTANCE = 
      GWT.create(CodePrettifyBundle.class);
  
  @Source("prettify.js")
  TextResource prettifyScript();
  
  @NotStrict
  @Source("prettify.css")
  CssResource prettifyStylesheet();
}

Abb. 3: Einbinden der JavaScript-Bibliothek „Google Code Prettify“
Geschrieben von
Frank Wisniewski
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.