[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}"/>
}}}