JSF-JQuery 2 ist der nächste logische Schritt nach JSF-JQuery. Die Version 2 automatisiert die API noch weiter, sodass die Implementierung von kundenspezifischen Komponenten sich auf das Wesentliche konzentrieren kann.
Component Class#
Während Version 1 noch auf JQueryComponent basiert, bringt Version 2 eine weitere Ableitung JQueryWidget mit. Die bisherige Klasse kann für mehr Flexibilität weiter genutzt werden, einfacher und bequemer geht es mit der neuen Klasse.Einblick in JQueryWidget#
Zunächst die Basisklasse JQueryComponent, diese implementiert, wie gewöhnlich, die JSF-Klasse UIComponentBase und zusätzlich das Interfaces JQueryListener:public abstract class JQueryComponent extends UIComponentBase implements JQueryListener
JQueryListener ist ein Interfaces für einen extrem vereinfachten JQuery-JSON-Request Lifecycle:
public interface JQueryListener extends FacesListener { Object processRequest(FacesContext context, Map<String, String> parameters); void renderResponse(FacesContext context, Object result) throws IOException; }
Die Aufgabe von processRequest ist es, den JQuery-Request entgegenzunehmen und ein Antwortobjekt zu liefern. Dieses Objekt wird JSON-Serialisiert und an das JQuery-Widget zurückgeschickt. JQueryWidget hat hier eine Default-Implementierung:
public Object getValue() { return getStateHelper().eval("value"); } public void setValue(Object value) { getStateHelper().put("value", value); } @Override public Object processRequest(FacesContext context, Map<String, String> parameters) { return getValue(); }
Erklärung: Bei einem Javascript seitigen Request antwortet die Komponente damit, indem sie das Objekt aus dem Value-Binding des Attributs "value" JSON-Serialisiert und als Response zurück schickt.
Hinweis: Für komplexere Antworten oder generierte Objekte kann die Methode überschrieben werden.
Widget Encoding#
JQueryWidget liefert nun auch eine Default-Implementierung für die JSF-Methoden encodeBegin und encodeEnd:@Override public void encodeBegin(FacesContext context) throws IOException { super.encodeBegin(context); ResponseWriter writer = context.getResponseWriter(); writer.startElement("div", this); writer.writeAttribute("id", getClientId(), "id"); [...] } @Override public void encodeEnd(FacesContext context) throws IOException { ResponseWriter writer = context.getResponseWriter(); writer.startElement("script", this); writer.writeAttribute("type", "text/javascript", null); String options = getOptions(); writer.writeText("$(function() {", this, null); writer.writeText("$.id('" + getClientId() + "')." + getWidgetName() + "(" + options + ");", this, null); writer.writeText("});", this, null); writer.endElement("script"); writer.endElement("div"); super.encodeEnd(context); }
Erklärung: Zunächst wird ein HTML DIV-Element geschrieben, welches die Client-Id der JSF-Komponente enthält. So kann später die Zuordnung zur Komponente wiederhergestellt werden. Dann wird ein Javascript-Block geschrieben, der die JQuery-Komponente auf dem gerenderten DIV instantiiert. Im Konstruktor werden die Parameter aus der Methode getOptions übergeben, sodass diese später im Javascript unter this.options verfügbar sind.
JQueryResourceHandler#
Der Request einer JSF-JQuery Komponente wird nicht vom JSF-Lifecycle entgegengenommen, sondern von einem JSF-Resource-Handler namens JQueryResourceHandler. Es handelt sich um einen Resource-Handler, ähnlich wie bei Image-, Script- oder CSS-Requests.Dieser "simuliert" einen echten JSF-Lifecycle, jedoch ohne den teueren Komponentenbaum zu rekonstruieren. Damit sind Lifecycle Phasen, Scopes (inklusive View-Scope) und damit EL-Expressions verfügbar.
Javascript Widget#
Auf der Javascript-Seite wird das Basis-Widget ext.JQueryWidget mitgeliefert, welches die Kommunikation mit der Komponente übernimmt. Über View-Id, View-State und Client-Id der Komponente wird die Verbindung zur entsprechenden Instanz auf der Java-Seite hergestellt.Action und Onsuccess#
Um einen JSON-Request zu starten, wird die Funktion "action" aufgerufen. Es können Parameter übergeben werden, ist jedoch nicht erforderlich. Es wird ein JSON-Request gestartet, der Response kommt über die Funktion onsuccess herein.Funktion | Erklärung |
---|---|
action(options) | Starten eines JSON-Requests an den Server. |
onsuccess(request, response, options) | Empfang des Response vom entsprechenden action-Request. |
onfail(request, response, options, status) | Diese Funktion hat eine Default-Implementierung, die den Fehler in einem Primefaces Growl oder Message-Dialog anzeigt. |
Beispiele#
Vieles lässt sich leichter anhand von Beispielen erklären.Anychart Komponente#
Eine typische Implementierung sieht so aus:@FacesComponent(namespace = JQueryComponent.NAMESPACE, createTag = true) @ResourceDependencies({ @ResourceDependency(name = "jquery/jquery.js", library = "primefaces"), @ResourceDependency(name = "jsf-jquery.js", library = "jquery-js"), @ResourceDependency(name = "anychart.min.js", library = "anychart"), @ResourceDependency(name = "anychart-themes.min.js", library = "anychart"), @ResourceDependency(name = "any-chart.js", library = "anychart") }) public class AnyChart extends JQueryWidget { public String getTheme() { return (String)getStateHelper().eval("theme"); } public void setTheme(String theme) { getStateHelper().put("theme", theme); } @Override public String getOptions(Object... properties) { return super.getOptions("theme"); } }
Erklärung: getTheme und setTheme sind typische Tag-Attribute von JSF. In der Methode getOptions können Attribute, Events und Parameter angegeben werden, die automatisch an die JQuery-Komponente übergeben werden.
Javascript Widget#
Javascript-Seitig wird mit der Widget-Function von JQuery von ext.JQueryWidget abgeleitet:$.widget("ext.AnyChart", $.ext.JQueryWidget, { options: { theme: "defaultTheme" }, _create: function() { this._super(); this.refresh(); }, _destroy: function() { this.cleanup(); this._super(); }, _setOptions: function(options) { this._super(options); this.refresh(); }, refresh: function() { this.action(); }, cleanup: function() { if (this.chart) { this.chart.dispose(); this.chart = null; } }, onsuccess: function(request, response, options) { this.cleanup(); response.chart.container = this.element.clientId(); anychart.theme(anychart.themes[response.theme || this.options.theme]); this.chart = anychart.fromJson(response); this.chart.draw(); } }
Erklärung: Nach dem Initialisieren des Widget wird in der Funktion refresh die Funktion action aus der Superklasse ext.JQueryWidget aufgerufen. Diese erzeugt den JSON-Request an die Java-Komponente. Die Antwort kommt über den Call-Back onsuccess zurück. Interessant ist vor allem der Parameter "repsonse", darin befinden sich die Daten, die vom Request an die Komponente zurückgeliefert wurden.
Damit ist die Integration der reinen Javascript-Bibliothek Anychart in eine JSF-Komponente abgeschlossen. Die Komponente kann nun verwendet werden:
<j:anyChart value="#{anyChartController.chart}"/>