Value-Change-Listener zum Initialisieren der Bundesländer
 
  Umschalten der Kreditkartendaten mittels JavaScript-API
 
  MyGourmet 14 mit Ajax-Status
 
  Lebenszyklus mit Ajax
 
  Kompositkomponente ajaxStatus
 
  Kompositkomponente ajaxPoll
 
  JavaScript-Debugging in Firebug
 
  JavaScript für Kompositkomponente ajaxStatus
 
  JavaScript für Kompositkomponente ajaxPoll
 
  h:selectOneMenu mit f:ajax und Value-Change-Listener
 
  h:selectOneMenu mit f:ajax und Ajax-Listener
 
  Firebug mit aktualisierter Komponente
 
  f:ajax im Einsatz: Beispiel 4
 
  f:ajax im Einsatz: Beispiel 3
 
  f:ajax im Einsatz: Beispiel 2
 
  f:ajax im Einsatz: Beispiel 1
 
  Erweiterte Version des Debug-Phase-Listeners mit Ajax-Support
 
  Einsatz von f:ajax zum Umschalten der Kreditkartendaten
 
  Die Komponente ajaxStatus im Einsatz
 
  Der HTTP-Debugger Fiddler protokolliert den HTTP-Verkehr
 
  Callback-Funktion für Ajax-Ereignisse
 
  Beispiel für resetValues
 
  Beispiel für delayvon f:ajax
 
  Beispiel 5 mit Ajax über JavaScript
 
  Ansicht mit Attributen und Methoden in Firebug
 
  Ajax-Listener zum Initialisieren der Bundesländer
 
  Ajax-Kompositkomponente collapsiblePanelim Einsatz
 
  Ajax in der Kompositkomponente collapsiblePanel

7 Ajax und JSF

Ajax hat in den letzten Jahren enorm an Bedeutung gewonnen und ist aus der Webentwicklung nicht mehr wegzudenken. Mit der stark zunehmenden Nutzung des Internets in den letzten Jahren stieg auch der Wunsch nach einer interaktiveren und umfangreicheren Benutzung von Webseiten, wie sie im Bereich herkömmlicher Desktop-Applikationen bereits seit vielen Jahren üblich und Benutzern vertraut ist. Diese Lücke soll durch neue Ansätze in der Webtechnologie geschlossen werden. Das grundlegende Ziel muss lauten, das Web noch stärker an die oft sehr unterschiedlichen Bedürfnisse der Benutzer anzupassen.
Geschichte des Web: In der Geschichte des World Wide Web bestand der erste technologische Ansatz (Web in der Version 1.0) darin, dem Benutzer rein statische HTML-Seiten zu präsentieren. Diese waren schwer wartbar, boten kein dynamisches Verhalten und wiesen für den Benutzer eine oft undurchsichtige Benutzerführung auf.
Die nächste Phase wurde durch den Einsatz von dynamischen Inhalten eingeleitet. Der Benutzer bekommt nach Interaktion mit dem Server dynamische und für ihn spezifische Inhalte aufbereitet. Diesem Ansatz für das World Wide Web wird manchmal die Versionsnummer 1.5 zugeschrieben.
Web 2.0: Der Begriff des Web in der Version 2.0 ist eine logische Weiterentwicklung, die den Bedürfnissen der Benutzer folgt. Erstmals im Oktober 2004 für eine Konferenz gebraucht, steht er für vernetzte und personalisierte Plattformen, die Desktop-Applikationen in vielen Bereichen ersetzen sollen.
Web 2.0 ist ein Begriff, dessen Definition recht breit gefasst ist. Ajax ist eine jener Technologien, die die Realisierung von Anwendungen des Web in der Version 2.0 unterstützen sollen. Im Laufe der letzten Jahre erfreute sich Ajax einer immer größeren Beliebtheit, wobei nicht zuletzt auch Google mit seinen auf dieser Technologie aufsetzenden Applikationen zu einem rasanten Aufschwung beitrug.
Bevor wir uns in Abschnitt [Sektion:  Ajax ab JSF 2.0] auf die Details und erste praktische Beispiele stürzen, werden wir in Abschnitt [Sektion:  Einführung in Ajax -- "`Asynchronous JavaScript And XML"'] den Begriff Ajax etwas genauer unter die Lupe nehmen. In den Abschnitten [Sektion:  Ajax in Kompositkomponenten] und [Sektion:  Eigene Ajax-Komponenten] ziehen wir dann alle Register und setzen Ajax mit Kompositkomponenten ein. Abschnitt [Sektion:  MyGourmet 14: Ajax] zeigt anschließend die Integration von Ajax in MyGourmet . Zu guter Letzt präsentieren wir in Abschnitt [Sektion:  Werkzeuge für den Ajax-Entwickler] noch einige Werkzeuge, die Ihnen bei der Arbeit an Ajax-Anwendungen unter die Arme greifen können.

7.1 Einführung in Ajax -- "`Asynchronous JavaScript And XML"'

Ajax ist an sich keine eigenständige Technologie im Webbereich, sondern vielmehr ein Sammelbegriff für eine Vielzahl von Technologien, die bereits seit mehreren Jahren eingesetzt werden. Der Name Definition von Ajax: wurde zum ersten Mal 2005 in einem Essay über neue Entwicklungen und zukünftige Trends im Bereich von Webapplikationen erwähnt http://adaptivepath.com/publications/essays/archives/000385.php: . Ajax steht für " A synchronous J avaScript A nd X ML" und setzt auf Basistechnologien wie XHTML, Cascading Style Sheets (CSS), Document Object Model (DOM), XML, JavaScript und das XMLHttpRequest-Objekt auf.
Der zentrale Baustein einer Ajax-Applikation ist die Skriptsprache JavaScript. Folglich muss JavaScript im Browser aktiviert sein, um die Ajax-Funktionalität nutzen zu können. Üblicherweise greift jede mit Ajax entwickelte Webseite auf eine bereits vorhandene JavaScript-Bibliothek wie etwa jQuery zurück. Eine solche Bibliothek bietet Code für die einfache Verwendung des XMLHttpRequest-Objekts an und behandelt oft auch Unterschiede zwischen einzelnen Browsern.
Beim Einsatz von Ajax in einer Webapplikation gibt es Unterschiede hinsichtlich der Intensität der Verwendung. Die einfachste Möglichkeit ist die minimale Integration von Ajax zur Optimierung der herkömmlichen Abläufe einer Applikation. Der Gebrauch mehrerer Ajax-basierter Komponenten ist schon eine Stufe darüber anzusiedeln. Als letzter Schritt ist die Realisierung einer kompletten Applikation auf Basis von JavaScript und Ajax möglich. Für den Bau solcher Anwendungen ist JSF aber nicht mehr die optimale Wahl - dazu eignen sich reine JavaScript-Bibliotheken oder das Google Web Toolkit-(GWT-) Framework besser. Wir werden uns daher nur mit den ersten beiden Ansätzen beschäftigen.
Der Vorteil einer typischen Ajax-Webanwendung ist die wesentlich flüssigere Interaktion zwischen dem Browser und dem Webserver. Möglich ist dies durch die Technik, Daten asynchron vom Server zu laden, während clientseitig das System nicht zum Stillstand kommen muss. Außerdem wird die Oberfläche des Browsers nur mit der angeforderten Information aktualisiert, also nicht komplett neu geladen.

7.2 Ajax ab JSF 2.0

Die rasante Entwicklung von Ajax ist natürlich auch an der JSF-Welt nicht spurlos vorübergegangen. Dass JSF und Ajax sich gut vertragen, bewiesen bereits vor JSF 2.0 eine ganze Reihe von Komponenten-bibliotheken und Ajax-Frameworks speziell für JSF. Jede dieser Lösungen funktionierte für sich genommen einwandfrei. Das Problem lag - wie bei vielen Entwicklungen der damaligen Zeit im JSF-Umfeld - an der fehlenden Spezifikation und der daraus entstandenen Inkompatibilität der unterschiedlichen Produkte. Obwohl alle Lösungen das gleiche Ziel - die Integration von JSF und Ajax - verfolgten, unterschieden sich die technische Umsetzung doch teils erheblich. Mit JSF 2.0 war dieses Problem Geschichte, da die Spezifikation eine Standardisierung der Ajax-Unterstützung beinhaltet.
JSF definiert ab Version 2.0 eine JavaScript-Bibliothek, die grundlegende Ajax-Operationen wie das Senden einer Anfrage und das Bearbeiten der Antwort abdeckt. Mit dieser standardisierten Schnittstelle ist gewährleistet, dass alle Komponenten clientseitig dieselbe Funktionalität einsetzen und sich nicht in die Quere kommen.
Diese JavaScript-API bildet auch die Grundlage für die Integration von Ajax in JSF-Anwendungen. JSF bietet grundsätzlich zwei verschiedene Ansätze, um Ansichten mit Ajax-Funktionalität auszustatten. Zum einen können Entwickler zum Absetzen einer Ajax-Anfrage direkt die Funktion der JavaScript-API aufrufen. Zum anderen gibt es mit dem Tag f:ajax eine deklarative Variante, um eine Komponente oder sogar einen ganzen Bereich einer Seitendeklaration mit Ajax-Funktionalität auszustatten.
Die Spezifikation kümmert sich allerdings nicht nur um die Clientseite. Auch die Bearbeitung einer Ajax-Anfrage am Server ist umfassend spezifiziert. JSF 2.0 erweiterte den Lebenszyklus so, dass zum Bearbeiten einer Ajax-Anfrage nur die relevanten Teile des Komponentenbaums ausgeführt ( Partial-View-Processing ) und gerendert ( Partial-View-Rendering ) werden.
Im Laufe dieses Abschnitts gehen wir auf die Details der Ajax-Integration in JSF ab Version 2.0 ein. Nach einem ersten Beispiel mit f:ajax in Abschnitt [Sektion:  Ein erstes Beispiel mit f:ajax] folgen in Abschnitt [Sektion:  f:ajax im Einsatz] noch weitere Beispiele und Abschnitt [Sektion:  Ereignisse und Listener in Ajax-Anfragen] zeigt, wie sich Ereignisse und Listener bei Ajax-Anfragen verhalten. Abschnitt [Sektion:  JavaScript-API] widmet sich anschließend der JavaScript-API für Ajax und Abschnitt [Sektion:  Partieller JSF-Lebenszyklus] gibt Einblicke in den partiellen Lebenszyklus. Abschließend zeigt Abschnitt [Sektion:  Eingabefelder zurücksetzen] noch wie und vor allem warum, ab JSF 2.2 Eingabefelder bei Ajax-Anfragen zurückgesetzt werden können.

7.2.1 Ein erstes Beispiel mit f:ajax

Als erstes Beispiel werden wir das Ein- und Ausblenden der Kreditkartendaten in der Ansicht editCustomer.xhtml auf Ajax umstellen. Bis jetzt löste ein Klick auf das Auswahlfeld "Kreditkarte angeben" über JavaScript ein Übermitteln des Formulars aus. Am Server fand dadurch natürlich ein Durchlauf des Lebenszyklus über den kompletten Komponentenbaum statt. Eigentlich wollen wir aber nur die Eigenschaft useCreditCard neu setzen und die zwei Labels und Eingabekomponenten der Kreditkartendaten abhängig davon ein- oder ausblenden.
Wir wollen erreichen, dass ein Klick auf das Auswahlfeld eine Ajax-Anfrage an den Server auslöst. Als Reaktion auf die Anfrage soll der registrierte Value-Change-Listener ausgeführt und der Bereich mit den Kreditkartendaten neu gerendert werden. JSF erleichtert uns ab Version 2.0 diese Aufgabe enorm. Mit dem Tag f:ajax kann eine Komponente mit Ajax-Funktionalität ausgestattet werden, indem es als Kind-Tag des mit Ajax-Unterstützung zu versehenden Elements in die Deklaration eingefügt wird. In unserem Beispiel ergibt sich die gewünschte Funktionalität durch das Hinzufügen des f:ajax -Elements zur Auswahlkomponente h:selectBooleanCheckbox . Listing Einsatz von f:ajax zum Umschalten der Kreditkartendaten zeigt die relevanten Teile der Deklaration editCustomer.xhtml . Aus Gründen der einfacheren Handhabung haben wir das Formular in zwei Bereiche geteilt: einen für die Basisdaten und einen für die Kreditkartendaten.
<h:form id="form">
  <h:panelGrid id="baseData" columns="2">
    ...
    <h:selectBooleanCheckbox id="useCreditCard"
        value="#{customerBean.customer.useCreditCard}"
        valueChangeListener=
            "#{customerBean.useCreditCardChanged}">
      <f:ajax render="ccData"/>
    </h:selectBooleanCheckbox>
  </h:panelGrid>
  <h:panelGrid id="ccData" columns="2">
    ...
  </h:panelGrid>
</h:form>
Das Hinzufügen des f:ajax -Tags bringt bereits den gewünschten Effekt: Wenn der Benutzer im Browser den Wert des Felds "Kreditkarte angeben" ändert, wird nicht mehr die gesamte Seite neu geladen, sondern nur noch der Bereich mit den Kreditkartendaten neu gezeichnet. Sie können sich davon mit einem Werkzeug wie Firebug (siehe Abschnitt [Sektion:  Firebug] ) überzeugen, indem Sie den Typ der abgesetzten Anfrage analysieren. Genauso gut können Sie aber auch die aktuelle Uhrzeit auf der Seite ausgeben - sie wird sich nicht ändern.
Die kleine Änderung hat einen durchschlagenden Effekt. Wie verarbeitet JSF das Tag f:ajax ? Der gewählte Ansatz ist so einfach wie mächtig. JSF setzt das Tag f:ajax beim Rendern unserer Auswahlkomponente als Aufruf der Funktion jsf.ajax.request() im Attribut onchange um. Mit dem Attribut render teilen wir JSF mit, welche Komponente neu gerendert werden soll. In unserem Fall ist das die Panel-Grid-Komponente mit der ID ccData .
Tipp: Auch mit Ajax muss weiterhin h:form in gewohnter Art und Weise eingesetzt werden.
Beachten Sie bitte auch, dass die h:selectBooleanCheckbox -Komponente nicht mehr immediate ist. Zuvor war das notwendig, da bei einem Klick auf das Auswahlfeld immer das komplette Formular im Lebenszyklus ausgeführt wurde und einige der anderen Formularfelder verpflichtend anzugeben sind, und damit unschöne Validierungsmeldungen eingeblendet wurden. Mit Ajax wird der Lebenszyklus nur noch partiell für die eine Komponente ausgeführt, es erscheinen keine Validierungsmeldungen für andere Komponenten.
Schreiben wir das f:ajax -Tag in Listing Einsatz von f:ajax zum Umschalten der Kreditkartendaten mit den Standardwerten für seine Attribute aus, so erhalten wir:
<f:ajax event="valueChange" execute="@this" render="ccData"/>
Das Ergebnis der Langschreibweise ist identisch. Der Wert des Attributs event bestimmt das Ereignis, von dem die Ajax-Anfrage ausgelöst wird. Mögliche Werte dafür sind valueChange für Eingabekomponenten, action für Befehlskomponenten und alle anderen HTML-Ereignisse - allerdings ohne das Präfix on . Wir könnten also mit dem Wert click die Ajax-Anfrage genauso gut durch eine Klick auf das Auswahlfeld auslösen. Das Attribut execute definiert, welche Komponenten beim Abarbeiten des Lebenszyklus am Server bearbeitet werden. Die Konstante @this bezeichnet dabei die umschließende Komponente. Die Attribute event und execute können in unserem Fall weggelassen werden, da es sich bei den Werten valueChange und @this um die Defaultwerte für Eingabekomponenten handelt. Was JSF am Server als Reaktion auf diese Anfrage macht, zeigen wir Ihnen in Abschnitt [Sektion:  Partieller JSF-Lebenszyklus] .
Listing Umschalten der Kreditkartendaten mittels JavaScript-API zeigt die zweite Variante, eine Komponente mit Ajax auszustatten. Nachdem das Absenden einer Ajax-Anfrage immer über die standardisierte JavaScript-API läuft, kann die dafür zuständige Funktion jsf.ajax.request() auch direkt verwendet werden. Die Funktion übernimmt als Parameter das auslösende DOM-Element, das Ereignis und ein assoziatives Array mit Optionen. Mehr dazu in Abschnitt [Sektion:  JavaScript-API] .
<h:selectBooleanCheckbox id="useCreditCard"
    value="#{customerBean.customer.useCreditCard}"
    valueChangeListener="#{customerBean.useCreditCardChanged}"
    onchange="jsf.ajax.request(
        this, event, {render: 'form:ccData'});"/>
Tipp: Verwenden Sie in allen Seitendeklarationen h:head und h:body . Nur so kann JSF die für Ajax benötigten Skript-Ressourcen richtig einbinden.

7.2.2 f:ajax im Einsatz

Mit dem Tag f:ajax können eine ganze Reihe von Standardkomponenten in JSF mit Ajax-Verhalten ausgestattet werden. Genauer gesagt sind es alle Komponenten, die das Interface ClientBehaviorHolder implementieren, wodurch es relativ einfach möglich ist, auch eigene Komponenten oder solche aus Komponentenbibliotheken für f:ajax fit zu machen. Damit steht der Kompatibilität von Komponenten aus unterschiedlichen Quellen in Bezug auf Ajax nichts mehr im Wege.
f:ajax kann auf zwei Arten zum Einsatz kommen. Zum einen kann eine einzelne Komponente mit Ajax-Verhalten ausgerüstet werden, indem f:ajax als Kind-Tag eingefügt wird. Zum anderen ist es auch möglich, mit f:ajax einen ganzen Bereich einer Seite auf Ajax umzustellen.
Hier die wichtigsten Attribute des Tags f:ajax :
Sehen wir uns dazu einige kurze Beispiele an. Das erste Beispiel beinhaltet eine Form mit zwei Eingabefeldern für den Vornamen und den Nachnamen einer Person und einer Schaltfläche. Unterhalb der Schaltfläche befindet sich noch ein Textfeld, mit dem der komplette Name ausgegeben wird. Ohne Ajax bewirkt ein Klick auf die Schaltfläche einen normalen Submit mit anschließendem Neuaufbau der Ansicht. In diesem Fall ist im Textfeld erst nach dem Übermitteln der eingegebene Name sichtbar.
Listing f:ajax im Einsatz: Beispiel 1 zeigt das Beispiel bereits in einer mit Ajax erweiterten Variante. Das Tag f:ajax bewirkt, dass die Schaltfläche über Ajax das Ausführen der beiden Eingabekomponenten und das Rendern des Textfelds anstößt. Wenn Sie im Browser einen Vor- und Nachnamen eingeben und die Schaltfläche aktivieren, wird das Textfeld ohne Neuaufbau der Ansicht aktualisiert.
<h:form id="form">
  <h:panelGrid columns="1">
    <h:inputText id="first" value="#{test.first}"/>
    <h:inputText id="last" value="#{test.last}"/>
    <h:commandButton value="Show">
      <f:ajax execute="first last" render="name"/>
    </h:commandButton>
  </h:panelGrid>
  <h:outputText id="name" value="#{test.name}"/>
</h:form>
Das Attribut execute enthält die IDs first und last der beiden Eingabekomponenten und das Attribut render die ID name der Textkomponente. Diese Werte werden in der Ajax-Anfrage inkludiert und JSF führt den Lebenszyklus nur für die beiden Eingabekomponenten aus und rendert anschließend das Textfeld neu. Das Attribut event ist nicht explizit gesetzt, für h:commandButton ist aber als Defaultereignis action definiert.
Tipp: Komponenten, die mittels Ajax neu gerendert werden - deren IDs also im Attribut render von f:ajax vorkommen -, müssen immer im DOM der Seite vorhanden sein (siehe Abschnitt [Sektion:  Ajax in Kompositkomponenten] ).
Im zweiten Beispiel kommt ein zusätzliches f:ajax -Tag zum Einsatz, um einen ganzen Bereich der Deklaration mit Ajax-Verhalten auszustatten. Listing f:ajax im Einsatz: Beispiel 2 zeigt die erweiterte Deklaration.
<h:form id="form">
  <f:ajax render="name">
    <h:panelGrid columns="1">
      <h:inputText id="first" value="#{test.first}"/>
      <h:inputText id="last" value="#{test.last}"/>
      <h:commandButton value="Show">
        <f:ajax execute="first last" render="name"/>
      </h:commandButton>
    </h:panelGrid>
  </f:ajax>
  <h:outputText id="name" value="#{test.name}"/>
</h:form>
Da f:ajax im Attribut event kein Ereignis definiert, werden nur jene Komponenten innerhalb des Tags mit Ajax-Verhalten ausgestattet, die ein Defaultereignis haben. Konkret sind das die beiden Eingabekomponenten mit dem Ereignis valueChange . Das Ajax-Verhalten der Schaltfläche für das Defaultereignis bestimmt bereits das innere f:ajax -Tag und ändert sich deshalb nicht. Mit der zusätzlichen Ajax-Funktionalität wird jetzt jedes Mal, wenn sich im Browser eines der beiden Eingabefelder ändert, das Textfeld neu gerendert.
Tabelle tab:ajax-default-events zeigt eine Übersicht aller Standardkomponenten mit Defaultereignissen.
KomponenteDefaultereignis
h:commandButton action
h:commandLink action
h:inputText valueChange
h:inputTextarea valueChange
h:inputSecret valueChange
h:selectBooleanCheckbox valueChange
h:selectOneRadio valueChange
h:selectOneListbox valueChange
h:selectOneMenu valueChange
h:selectManyCheckbox valueChange
h:selectManyListbox valueChange
h:selectManyMenu valueChange
Listing f:ajax im Einsatz: Beispiel 3 zeigt das gleiche Beispiel wie Listing f:ajax im Einsatz: Beispiel 2 , mit dem Unterschied, dass im äußeren f:ajax -Tag das Attribut event auf dblclick gesetzt ist.
<h:form id="form">
  <f:ajax event="dblclick" render="name">
    <h:panelGrid columns="1">
      <h:inputText id="first" value="#{test.first}"/>
      <h:inputText id="last" value="#{test.last}"/>
      <h:commandButton value="Show">
        <f:ajax execute="first last" render="name"/>
      </h:commandButton>
    </h:panelGrid>
  </f:ajax>
  <h:outputText id="name" value="#{test.name}"/>
</h:form>
Durch diese kleine Änderung lösen jetzt alle Komponenten innerhalb f:ajax nach einem Doppelklick eine Ajax-Anfrage aus. Das gilt auch für das Panel-Grid und die Schaltfläche, die jetzt für zwei verschiedene Ereignisse mit Ajax-Verhalten ausgestattet ist.
Das nächste Beispiel in Listing f:ajax im Einsatz: Beispiel 4 ist wiederum nur eine Variante des bereits bekannten Formulars. Diesmal gibt es jedoch zwei Textfelder zur Ausgabe des Namens: Das erste liegt innerhalb der Form und hat die ID inner . Das zweite Textfeld liegt außerhalb und hört auf die ID outer . Das äußere Feld soll immer dann aktualisiert werden, wenn sich der Wert eines der Eingabefelder ändert, wohingegen das innere durch einen Klick auf die Schaltfläche aktualisiert werden soll. Das sollte an und für sich kein Problem darstellen. Wenn das Attribut render des inneren f:ajax -Tags auf inner und das des äußeren auf outer gesetzt wird, sollte sich doch genau dieses Verhalten einstellen. Tut es aber nicht - JSF beschwert sich schon beim Laden der Seite, dass die ID outer nicht existiert. Warum das so ist, klären wir gleich.
<h:form id="form">
  <f:ajax render=":outer">
    <h:panelGrid columns="1">
      <h:inputText id="first" value="#{test.first}"/>
      <h:inputText id="last" value="#{test.last}"/>
      <h:commandButton value="Show">
        <f:ajax execute="first last" render="inner"/>
      </h:commandButton>
    </h:panelGrid>
  </f:ajax>
  <h:outputText id="inner" value="#{test.name}"/>
</h:form>
<h:outputText id="outer" value="#{test.name}"/>
Das Problem wird vom Textfeld außerhalb der Form verursacht und liegt an der Berechnung der Client-IDs für Ajax-Anfragen. JSF setzt dazu die Methode UIComponent.findComponent() ein, die einen bestimmten Algorithmus zum Auffinden der Komponente mit der übergebenen ID anwendet. Dieser Algorithmus geht davon aus, dass die übergebene ID relativ zum nächsthöheren Naming-Container ist. In unserem Fall wird also versucht, die Komponente mit der ID outer als Kind der Form mit der ID form aufzulösen. Nachdem das Textfeld aber außerhalb liegt, kann das nicht funktionieren. Die Lösung dieses Problems ist einfach: findComponent() behandelt alle IDs mit einem führenden Doppelpunkt als absolute IDs, die beginnend beim Wurzelknoten des Komponentenbaums aufgelöst werden. Wir müssen also nur das render -Attribut im äußeren f:ajax -Tag auf :outer setzen, um die gewünschte Funktionalität zu erhalten.

7.2.3 Ereignisse und Listener in Ajax-Anfragen

In diesem Abschnitt werfen wir einen genaueren Blick auf Ereignisse und Listener in Ajax-Anfragen. Der Einsatz von Action-Methoden und Ereignisbehandlungsmethoden für Action- oder Value-Change-Events funktioniert grundsätzlich gleich wie in Nicht-Ajax-Anfragen.
Im Beispiel in Listing h:selectOneMenu mit f:ajax und Value-Change-Listener zeigen wir Ihnen, wie Sie mit einfachen Mitteln zwei h:selectOneMenu -Komponenten miteinander verknüpfen. Die Auswahlmöglichkeiten der zweiten Komponente sollen von der getätigten Auswahl in der ersten Komponente abhängen und über Ajax aktualisiert werden. Ein gutes Beispiel dafür ist die Auswahl eines Landes und eines dazu passenden Bundeslandes. Mit dem f:ajax -Tag verpassen wir der ersten Komponente das dazu nötige Ajax-Verhalten.
<h:selectOneMenu id="country" value="#{test.country}"
    valueChangeListener="#{test.countryChanged}">
  <f:selectItems value="#{test.countryItems}"/>
  <f:ajax render="state"/>
</h:selectOneMenu>
<h:selectOneMenu id="state" value="#{test.state}">
  <f:selectItems value="#{test.stateItems}"/>
</h:selectOneMenu>
Auf der h:selectOneMenu -Komponente zur Auswahl des Landes ist die Methode test.countryChanged als Value-Change-Listener registriert. Immer wenn ein Benutzer den Wert im Browser ändert, wird eine Ajax-Anfrage abgesetzt und der Lebenszyklus wird partiell für diese Komponente ausgeführt. Da der Benutzer den Wert geändert hat, ruft JSF auch den Value-Change-Listener auf. In dieser Methode wird zuerst die Liste der Bundesländer abhängig vom gewählten Land neu gesetzt. Dann wird noch das aktuell ausgewählte Bundesland auf null zurückgesetzt. Listing Value-Change-Listener zum Initialisieren der Bundesländer zeigt den relevanten Code. Die h:selectOneMenu -Komponente mit der ID state wird anschließend neu gerendert und im Browser mit den neuen Werten aktualisiert.
public void countryChanged(ValueChangeEvent ev) {
  state = null;
  updateStateItems((String)ev.getNewValue());
}
private void updateStateItems(String country) {
  if (country != null) {
    stateItems = stateItemsMap.get(country);
  } else {
    stateItems = new ArrayList<SelectItem>();
  }
}
Das aktuell ausgewählte Land wird in der Methode countryChanged aus dem übergebenen Ereignis und nicht aus der Eigenschaft country der Managed-Bean geholt. Der Value-Change-Listener wird ja bereits in der Process-Validations-Phase des Lebenszyklus aufgerufen und der vom Benutzer ausgewählte Wert ist daher noch nicht in die Eigenschaft zurückgeschrieben worden.
Als Alternative zum Value-Change-Listener kann hier auch ein Ajax-Listener zum Einsatz kommen. f:ajax bietet ja die Möglichkeit, im Attribut listener eine Method-Expression anzugeben. Listing h:selectOneMenu mit f:ajax und Ajax-Listener zeigt das Beispiel von oben mit einem Ajax-Listener. Der Value-Change-Listener wird in diesem Fall nicht mehr benötigt.
<h:selectOneMenu id="country" value="#{test.country}">
  <f:selectItems value="#{test.countryItems}"/>
  <f:ajax render="state" listener="#{test.countryChanged}"/>
</h:selectOneMenu>
<h:selectOneMenu id="state" value="#{test.state}">
  <f:selectItems value="#{test.stateItems}"/>
</h:selectOneMenu>
Eine Ajax-Listener-Methode muss einen Parameter vom Typ AjaxBehaviorEvent haben, wie Listing Ajax-Listener zum Initialisieren der Bundesländer zeigt. Der Inhalt der Methode unterscheidet sich in einem wesentlichen Punkt vom Value-Change-Listener in Listing Value-Change-Listener zum Initialisieren der Bundesländer . Nachdem JSF den Ajax-Listener erst in der Invoke-Application-Phase aufruft, ist der Wert in der Eigenschaft country bereits aktuell. Die Methode updateStateItems hat sich im Vergleich zu Listing Value-Change-Listener zum Initialisieren der Bundesländer nicht geändert.
public void countryChanged(AjaxBehaviorEvent ev) {
  state = null;
  updateStateItems(country);
}
In Abschnitt [Sektion:  Partieller JSF-Lebenszyklus] wird der partielle Lebenszyklus bei Ajax-Anfragen noch einmal genauer unter die Lupe genommen.

7.2.4 JavaScript-API

JSF definiert ab Version 2.0 eine JavaScript-Bibliothek als clientseitige Grundlage für die Integration von Ajax. Die Bibliothek ist in der Ressource mit dem Namen jsf.js in der Bibliothek javax.faces abgelegt. Wenn Sie Ajax deklarativ mit dem Tag f:ajax einsetzen, müssen Sie sich um das Einbinden dieser Ressource nicht kümmern. Nur bei einem direkten Einsatz der JavaScript-API müssen Sie Folgendes angeben:
<h:outputScript name="jsf.js" library="javax.faces"
    target="head"/>
Um die Skript-Ressource jsf.js selbst müssen Sie sich keine Gedanken machen. Sie gehört zum Standardlieferumfang von JSF und ist in den Jar-Dateien inkludiert. Auch beim Einsatz der JavaScript-API ist es wichtig, h:head und h:body in der Seitendeklaration zu verwenden. Nur so kann garantiert werden, dass das Skript richtig in der gerenderten Ausgabe am Browser ankommt.
Hier eine Auflistung der wichtigsten Funktionen der JavaScript-API:

7.2.4.1 Senden von Ajax-Anfragen

Die Methode jsf.ajax.request(source, event, options) ist das Herzstück der JavaScript-API von JSF. Sie allein ist für das Senden der asynchronen Ajax-Anfragen an den Server zuständig. Das Tag f:ajax ist nur eine einfachere Variante, um das Ajax-Verhalten auf deklarativem Weg in die Ansicht zu bringen. Beim Rendern macht JSF daraus wieder einen Aufruf der Funktion jsf.ajax.request() .
Mit den Parametern source und event werden das DOM-Element und das DOM-Ereignis übergeben, die für das Auslösen der Ajax-Anfrage verantwortlich sind. Der dritte Parameter options ist ein assoziatives Array, mit dem weitere Optionen als Schlüssel-Wert-Paare an die Funktion übergeben werden. Diese Optionen entsprechen weitestgehend den bereits bekannten Attributen aus f:ajax . Über execute werden IDs auszuführender Komponenten angegeben und über render IDs von Komponenten, die neu zu zeichnen sind. Während beim Einsatz von f:ajax in den meisten Fällen eine relative ID genügt, muss beim Aufruf der JavaScript-Funktion immer die komplette Client-ID angegeben werden. Die Funktion operiert ja direkt auf DOM-Ebene und kann keine relativen IDs über den Komponentenbaum von JSF auflösen.
Die Funktion jsf.ajax.request() baut die Ajax-Anfrage zusammen und schickt sie asynchron an den Server. Was JSF dort genau mit dieser Anfrage macht, verraten wir Ihnen in Abschnitt [Sektion:  Partieller JSF-Lebenszyklus] . Sobald die Antwort auf die Anfrage vom Server zurückkommt, wird im Erfolgsfall die Funktion jsf.ajax.response() aufgerufen, um die Antwort zu verarbeiten und gegebenenfalls Teile der Ansicht neu aufzubauen.
Listing Beispiel 5 mit Ajax über JavaScript zeigt noch einmal das Beispiel mit den beiden Auswahllisten aus dem letzten Abschnitt. Diesmal ist das Ajax-Verhalten allerdings direkt über die JavaScript-API realisiert. Genau wie vorher löst das Ändern des Werts in der ersten Komponente eine Ajax-Anfrage aus, die ein Neuzeichnen der zweiten Komponente veranlasst.
<h:form id="form">
  <h:selectOneMenu id="country" value="#{test.country}"
      valueChangeListener="#{test.changeCountry}"
      onchange="jsf.ajax.request(
          this, event, {render: 'form:state'})">
    <f:selectItems value="#{test.countryItems}"/>
  </h:selectOneMenu>
  <h:selectOneMenu id="state" value="#{test.state}">
    <f:selectItems value="#{test.stateItems}"/>
  </h:selectOneMenu>
</h:form>

7.2.4.2 Status und Fehler von Ajax-Anfragen behandeln

Mit der Funktion jsf.ajax.addOnEvent(callback) kann eine Callback-Funktion zum Behandeln des Status von Ajax-Anfragen registriert werden. Die Callback-Funktion bekommt ein Objekt mit genaueren Angaben zum Ereignis übergeben, wobei vor allem die Eigenschaft status interessant ist. Sie nimmt die Werte begin für das Ereignis, kurz bevor die Anfrage an den Server geschickt wird, complete für das Ereignis, nachdem die Antwort vom Server zurückkommt, und success für das Ereignis nach erfolgreichem Abschluss der Anfrage an. Die Callback-Funktion wird also dreimal für jede Ajax-Anfrage aufgerufen. Listing Callback-Funktion für Ajax-Ereignisse zeigt eine Funktion, die für jedes Ereignis eine Meldung ausgibt. In Abschnitt [Sektion:  Die Kompositkomponente mc:ajaxStatus] zeigen wir Ihnen, wie Sie mit einfachsten Mitteln eine Kompositkomponente zum Darstellen einer Ajax-Status-Meldung erstellen.
var processEvent = function processEvent(data) {
  if (data.status == "begin") {
    alert('Begin');
  } else if (data.status == "complete") {
    alert('Complete');
  } else if (data.status == "success") {
    alert('Success');
  }
}
jsf.ajax.addOnEvent(processEvent);
Eine über jsf.ajax.addOnEvent() registrierte Funktion wird bei jeder Ajax-Anfrage aufgerufen. addOnEvent() kann auch ohne Probleme mehrfach hintereinander aufgerufen werden, um mehr als eine Funktion zu registrieren. Wenn Sie eine Callback-Funktion für eine bestimmte Ajax-Anfrage benötigen, können Sie diese mit dem Attribut onevent von f:ajax oder beim Aufruf von jsf.ajax.request() als Option mit dem Schlüssel onevent registrieren.
Mit der Funktion jsf.ajax.addOnError(callback) kann eine Callback-Funktion registriert werden, die beim Auftreten eines Fehlers während der Bearbeitung einer Ajax-Anfrage aufgerufen wird. Die Callback-Funktion wird auch hier mit einem Objekt mit Detailinformationen versehen. Die Eigenschaft status kann die Werte httpError , serverError , malformedXML oder emptyResponse annehmen.
Eine über jsf.ajax.addOnError() registrierte Funktion wird im Fehlerfall bei jeder Ajax-Anfrage aufgerufen. addOnError() kann auch ohne Probleme mehrfach hintereinander aufgerufen werden, um mehr als eine Funktion zu registrieren. Wenn Sie eine Callback-Funktion für eine bestimmte Ajax-Anfrage benötigen, können Sie diese mit dem Attribut onerror von f:ajax oder beim Aufruf von jsf.ajax.request() als Option mit dem Schlüssel onerror registrieren.

7.2.5 Partieller JSF-Lebenszyklus

Eines der entscheidenden Features einer vernünftigen Ajax-Integration ist das partielle Ausführen ( Partial-View-Processing ) und Rendern ( Partial-View-Rendering ) des Komponentenbaums. Als Reaktion auf eine Ajax-Anfrage soll ja im ersten Schritt der Lebenszyklus nicht für den kompletten Komponentenbaum, sondern nur für die in der Anfrage spezifizierten Komponenten ausgeführt werden. Im zweiten Schritt wird als Ergebnis der Anfrage ein weiterer Teil des Komponentenbaums, der nicht mit dem ersten Teil übereinstimmen muss, gerendert.
JSF unterstützt ab Version 2.0 das partielle Ausführen und Rendern direkt mit dem Standardlebenszyklus. Dazu ist der Lebenszyklus, wie in Abbildung Lebenszyklus mit Ajax zu erkennen, in die zwei logischen Bereiche Ausführen und Rendern aufgeteilt.
Abbildung:Lebenszyklus mit Ajax
Welche Komponenten in den beiden Bereichen zum Einsatz kommen, wird über die Parameter der Ajax-Anfrage bestimmt. Deren Werte entsprechen den Werten der f:ajax -Attribute execute und render beziehungsweise den gleichnamigen Parametern im assoziativen Array der Funktion jsf.ajax.request() .
Tabelle tab:ajax-request-params zeigt die wichtigsten Parameter der Ajax-Anfrage aus dem Einführungsbeispiel in Abschnitt [Sektion:  Ein erstes Beispiel mit f:ajax] . JSF weiß anhand des Parameters javax.faces.partial.ajax , dass es sich um eine Ajax-Anfrage handelt und dass der Lebenszyklus partiell ausgeführt werden muss. Der Parameter javax.faces.source gibt an, welche Komponente die Anfrage ausgelöst hat, und die beiden restlichen Parameter bestimmen, welche Komponenten ausgeführt und gerendert werden.
ParameterWert
javax.faces.partial.ajax true
javax.faces.source form:useCreditCard
javax.faces.partial.execute form:useCreditCard
javax.faces.partial.render form:grid
Sie müssen sich allerdings keine Gedanken um diese Parameter machen. JSF erledigt das Erstellen und Abschicken der Ajax-Anfrage automatisch im Hintergrund. Sie müssen lediglich spezifizieren, welche Komponenten ausgeführt und gerendert werden sollen - und das auch nur, wenn sich diese Daten von den Defaultwerten unterscheiden.

7.2.6 Ajax-Queue kontrollieren

JSF 2.2: Mit JSF 2.2 ist es möglich, die clientseitige Queue der Ajax-Anfragen zu beeinflussen. Das neue Attribut delay von f:ajax erlaubt das Verzögern von Ajax-Anfragen um den in Millisekunden angegebenen Wert. Falls während dieser Zeitspanne mehrere Ajax-Anfragen auftreten, wird immer nur die aktuellste verarbeitet. Diese Funktionalität ist wie maßgeschneidert für Ajax-Anfragen, die von Tastaturereignissen ausgelöst werden. Das klassische Einsatzszenario dafür ist das automatische Vervollständigen von Benutzereingaben.
Das Beispiel in Listing Beispiel für delayvon f:ajax zeigt eine Eingabekomponente, die bei jedem Tastendruck des Benutzers eine Ajax-Anfrage auslöst. Mit dem Wert 300 für delay ist gewährleistet, dass der Server nicht mit Anfragen bombadiert wird, wenn der Benutzer schnell tippt. Treten innerhalb der angegebenen 300 Millisekunden mehrere Ajax-Anfragen auf, wird immer nur die aktuellste verarbeitet.
<h:inputText value="#{bean.product}">
  <f:ajax event="keyup" render="result" delay="300"/>
</h:inputText>
<h:panelGroup id="result">
  <ui:repeat value="#{bean.products}" var="p">
    #{p}<br/>
  </ui:repeat>
</h:panelGroup>

7.2.7 Eingabefelder zurücksetzen

JSF 2.2: Mit dem neuen Attribut resetValues von f:ajax lässt sich ab JSF 2.2 ein bereits länger bekanntes Problem elegant lösen. In einigen Fällen kann JSF während Ajax-Anfragen den Wert von Eingabekomponenten nur dann aktualisieren, wenn er zuvor explizit zurückgesetzt wurde. Dazu muss bei f:ajax das neue Attribut resetValues auf true gesetzt werden. In diesem Fall setzt JSF vor der Render-Phase alle im Attribut render angegebenen Eingabekomponenten durch einen Aufruf der Methode UIViewRoot.resetValues zurück.
Warum das notwendig sein kann, zeigt das Beispiel in Listing Beispiel für resetValues . Der Code sieht auf den ersten Blick einwandfrei aus. Bei näherer Betrachtung zeigt sich aber ein potenzielles Problem.
<h:form id="form">
  <h:messages id="msgs"/>
  <h:inputText id="v1" value="#{bean.value1}"/>
  <h:commandLink value="+1" action="#{bean.incValue1}">
    <f:ajax render="v1"/>
  </h:commandLink>
  <h:inputText id="v2" value="#{bean.value2}">
    <f:validateLongRange minimum="10"/>
  </h:inputText>
  <h:commandButton value="Save">
    <f:ajax execute="v1 v2" render="msgs v1 v2"/>
  </h:commandButton>
</h:form>
Solange der Benutzer nur den "+1"-Link klickt, funktioniert alles vorbildlich. Während der Ajax-Anfrage wird zuerst in der Action-Methode die Eigenschaft value1 um eins erhöht und dann das Eingabefeld mit dem neuen Wert gerendert.
Die Probleme beginnen erst, wenn der Benutzer die Schaltfläche zum Speichern mit einem ungültigen Wert für das zweite Eingabefeld betätigt. In diesem Fall schlägt die Validierung für die Komponente mit der ID v2 fehl. Nachdem wir aber die ID von h:messages vorsorglich in das Attribut render mit aufgenommen haben, bekommt der Benutzer wie erwartet eine Fehlermeldung zu sehen.
So weit funktioniert alles wie erwartet, doch jetzt wird bei einem Klick auf den "+1"-Link der aktualisierte Wert nicht mehr angezeigt (obwohl er tatsächlich erhöht wird). Der Grund dafür ist schnell gefunden. Während der Anfrage mit dem ungültigen Wert für v2 hat JSF in der Process-Validations-Phase für beide Komponenten den Local-Value gesetzt. Wegen des Validierungsfehlers wurde dieser aber nie zurückgesetzt. Nachdem der Local-Value beim Rendern immer Vorrang gegenüber dem Wert aus dem Modell hat, wird der nur im Modell aktualisierte Wert der Eigenschaft value1 nicht angezeigt.
Genau dieses Problem lässt sich durch das Setzen von resetValues vermeiden. Folgendes Beispiel zeigt die einwandfrei funktionierende Variante des "+1"-Links.
<h:commandLink value="+1" action="#{bean.incValue1}">
  <f:ajax render="v1" resetValues="true"/>
</h:commandLink>

7.3 Ajax in Kompositkomponenten

Im nächsten Beispiel werden wir die in Abschnitt Sektion:  Die Komponente mc:collapsiblePanel erstellte Kompositkomponente collapsiblePanel auf Ajax umstellen. In der aktuellen Variante wird bei jedem Klick auf das Icon zum Ein- und Ausklappen des Inhalts die komplette Seite neu aufgebaut - ein nicht mehr zeitgemäßes Verhalten. Vielmehr soll bei einem Klick auf das Icon nur der Inhalt des Panels angezeigt oder ausgeblendet werden.
Nichts leichter als das. Wie schon im letzten Beispiel lässt sich die Ajax-Unterstützung mit einer einzigen Zeile realisieren. Nachdem wir das von der Komponente h:commandButton ausgelöste Übermitteln der Seite auf Ajax umstellen wollen, fügen wir dort das Tag f:ajax hinzu. Listing Ajax in der Kompositkomponente collapsiblePanel zeigt den Implementierungsteil der Kompositkomponente mit Ajax-Unterstützung.
<cc:implementation>
  <h:panelGroup layout="block"
      styleClass="collapsiblePanel-header">
    <h:commandButton id="toggle"
        actionListener="#{cc.toggle}"
        styleClass="collapsiblePanel-img"
        image="#{resource[cc.collapsed ?
           'mygourmet:toggle-plus.png' :
           'mygourmet:toggle-minus.png']}">
      <f:ajax render="@this panel-content"/>
    </h:commandButton>
    <cc:renderFacet name="header"/>
  </h:panelGroup>
  <h:panelGroup id="panel-content" layout="block">
    <h:panelGroup rendered="#{!cc.collapsed}">
      <cc:insertChildren/>
    </h:panelGroup>
  </h:panelGroup>
</cc:implementation>
In der Render-Phase des partiellen Lebenszyklus müssen wir in diesem Fall zwei Komponenten neu rendern lassen. Zum einen die Panel-Group mit dem Inhalt der Komponente und zum anderen auch die Schaltfläche selbst, da sich das Icon abhängig vom Einklappzustand ändert. Das gleichzeitige Rendern mehrerer Komponenten stellt JSF erwartungsgemäß vor keine Probleme. Das Attribut render nimmt eine beliebige Anzahl mit Leerzeichen getrennter IDs auf, wobei auch hier die Konstante @this für die umschließende Komponente steht.
Wie Sie vielleicht schon bemerkt haben, ist im Vergleich zur Nicht-Ajax-Version das Tag cc:insertChildren von zwei h:panelGroup -Tags umschlossen. Es handelt sich dabei nicht um einen Fehler, sondern um eine notwendige Maßnahme, damit Ajax richtig funktioniert. Die Erklärung ist einleuchtend: Da als Antwort auf die Ajax-Anfrage die Panel-Group mit dem Bezeichner panel-content neu gerendert und im Browser ersetzt wird, muss sie dort immer existieren. Mit nur einer Panel-Group würde die gerenderte Ausgabe nach dem ersten Einklappen aus dem DOM verschwinden und könnte nicht mehr eingeblendet werden.
Das f:ajax -Tag in Listing Ajax in der Kompositkomponente collapsiblePanel ist eine abgekürzte Form des folgenden Tags:
<f:ajax event="action" execute="@this"
    render="@this panel-content"/>
Wie schon im letzten Beispiel können die Attribute event und execute auch hier zugunsten der Standardwerte entfallen. Befehlskomponenten definieren action als Defaultereignis für f:ajax , falls kein anderer Wert angegeben wird.
Doch damit noch nicht genug. Wir gehen noch einen Schritt weiter und stellen auch einen Teil des in die Kompositkomponente eingefügten Inhalts auf Ajax um. In der Ansicht showCustomer.xhtml befindet sich die Liste der Adressen eines Kunden in einer Kompositkomponente vom Typ dataTable (siehe Abschnitt Sektion:  Die Komponente mc:dataTable ). Die Tabelle ist ihrerseits in eine collapsiblePanel -Komponente eingebettet. In der Tabelle gibt es für jede Adresse eine h:commandLink -Komponente zum Löschen der Adresse. Ein Klick auf diesen Link soll eine Ajax-Anfrage auslösen und das Neuzeichnen der Tabelle veranlassen.
Listing Ajax-Kompositkomponente collapsiblePanelim Einsatz zeigt die relevanten Teile der Seitendeklaration show-Customer.xhtml mit dem auf Ajax umgestellten Link.
<mc:collapsiblePanel id="addressPanel"
    collapsed="#{customerBean.collapsed}">
  <f:facet name="header">
    <h3>#{msgs.title_addresses}</h3>
  </f:facet>
  <mc:dataTable id="addresses" var="address"
      value="#{customerBean.customer.addresses}">
    ...
    <h:column>
      <h:commandLink action="#{addressBean.edit(address)}"
          value="#{msgs.edit}"/> 
      <h:commandLink value="#{msgs.delete}"
          action="#{customerBean.deleteAddress(address)}">
        <f:ajax render=":form:addressPanel:addresses"/>
      </h:commandLink>
    </h:column>
  </mc:dataTable>
</mc:collapsiblePanel>
Mit f:ajax bekommt die Linkkomponente das Ajax-Verhalten eingeimpft. Wenn der Benutzer den Link im Browser aktiviert, wird eine Ajax-Anfrage an den Server geschickt. JSF führt den Lebenszyklus für die h:commandLink -Komponente aus und rendert anschließend die Tabelle neu. Beachten Sie bitte die Form der ID im Attribut render : Der führende Doppelpunkt kennzeichnet eine absolute ID. Dieser Schritt wird notwendig, wenn zwischen der auslösenden und der zu rendernden Komponente mehrere Naming-Container liegen.
Dieses Beispiel zeigt sehr schön, wie gut die einzelnen Features in JSF miteinander harmonieren. Die Integration von Ajax in Kompositkomponenten stellt eine einfache Möglichkeit dar, Komponenten mit nach außen transparenter Ajax-Unterstützung zu erstellen.

7.4 Eigene Ajax-Komponenten

In diesem Abschnitt zeigen wir Ihnen anhand von zwei Kompositkomponenten, wie einfach, aber zugleich mächtig die Ajax-Integration von JSF ist. Die Komponenten ajaxStatus und ajaxPoll nutzen direkt die JavaScript-API, um fortgeschrittene Ajax-Funktionalität anzubieten.

7.4.1 Die Kompositkomponente mc:ajaxStatus

Durch den vermehrten Einsatz von Ajax reduziert sich die Anzahl der Anfragen, die ein komplettes Neuladen der Ansicht nach sich ziehen. Neben den vielen Vorteilen, die Ajax mit sich bringt, gibt es auch einen kleinen Nachteil. Der Browser zeigt keinerlei Statusmeldung an, während eine Ajax-Anfrage bearbeitet wird. Bei jedem kompletten Seitenaufbau sieht der Benutzer einen Fortschrittsbalken oder eine Meldung, die Auskunft über den Status des Seitenaufbaus geben - nicht so bei Ajax-Anfragen. Und obwohl Ajax die Zeit zwischen Anfrage und Antwort erheblich verkürzt, kann es doch zu unklaren Situationen für den Benutzer kommen. Aus diesem Grund erstellen wir eine Komponente, die eine Statusmeldung anzeigt, solange eine Ajax-Anfrage aktiv ist.
Mit den Informationen über die JavaScript-API aus Abschnitt [Sektion:  JavaScript-API] ist es ein Leichtes, eine solche Komponente zu erstellen. Wir benötigen im Grunde nichts weiter als eine Statusmeldung und eine JavaScript-Funktion, die mit jsf.ajax.addOnEvent() als Callback registriert wird und die Statusmeldung dynamisch ein- und ausblendet. Das packen wir dann gemeinsam in eine Kompositkomponente und fertig ist die Ajax-Status-Komponente. Listing Kompositkomponente ajaxStatus zeigt die Deklaration.
<cc:interface>
  <cc:attribute name="text" default="Loading"/>
  <cc:attribute name="style"/>
  <cc:attribute name="styleClass"
      default="ajax-progress"/>
</cc:interface>
<cc:implementation>
  <h:outputStylesheet library="mygourmet"
      name="components.css"/>
  <h:outputScript name="jsf.js" library="javax.faces"
      target="head"/>
  <h:outputScript name="ajaxStatus.js"
      library="mygourmet" target="head"/>
  <script type="text/javascript">
    registerAjaxStatus('#{cc.clientId}:msg');
  </script>
  <div id="#{cc.clientId}:msg" class="#{cc.attrs.styleClass}"
      style="display: none;#{cc.attrs.style}">
    #{cc.attrs.text}
  </div>
</cc:implementation>
Die Schnittstelle der Komponente ist nicht besonders spannend. Sie besteht nur aus den drei Attributen text für die Statusmeldung sowie style und styleClass , um das Erscheinungsbild an eigene Bedürfnisse anzupassen.
Im Implementierungsteil werden zuerst benötigte Ressourcen geladen. Dazu zählen das Stylesheet components.css , die JavaScript-Bibliothek jsf.js und das Skript ajaxStatus.js mit der Funktionalität der Komponente. Anschließend wird über einen Aufruf der in ajax-Status.js definierten Funktion registerAjaxStatus() ein Callback für die aktuelle Komponente registriert. Als Parameter bekommt diese Funktion die ID des div -Elements mit der Statusmeldung. Schließlich soll dieses Element während einer Ajax-Anfrage ein- und dann wieder ausgeblendet werden. Beachten Sie bei der ID einmal mehr den Zugriff auf #{cc.clientId} . Nur so ist gewährleistet, dass auch wirklich die von JSF gerenderte Client-ID zum Einsatz kommt. Das Ein- und Ausblenden ist ganz einfach über die CSS-Eigenschaft display realisiert, wobei das Element initial mit display: none im Attribut style versteckt ist.
Der eigentlich interessante Aspekt der Komponente ist aber der in Listing JavaScript für Kompositkomponente ajaxStatus dargestellte JavaScript-Code.
function processAjaxUpdate(msgId) {
  function processEvent(data) {
    var msg = document.getElementById(msgId);
    if (data.status == "begin") {
      msg.style.display = '';
    } else if (data.status == "success") {
      msg.style.display = 'none';
    }
  }
  return processEvent;
};
function registerAjaxStatus(msgId) {
  jsf.ajax.addOnEvent(processAjaxUpdate(msgId));
}
Die Funktion registerAjaxStatus() registriert mit einem Aufruf von jsf.ajax.addOnEvent() eine Callback-Funktion, um das Element mit der übergebenen ID ein- und auszublenden. Es wird allerdings nicht die Funktion processAjaxUpdate() registriert, wie es auf den ersten Blick scheinen kann, sondern deren innere Funktion processEvent() . Durch diese Schachtelung von Funktionen ist es möglich, von außen die ID des Elements zu übergeben, wohingegen die innere Funktion den Parameter data bekommt. Da für die innere Funktion eine Closure erstellt wird, geht die übergebene ID nicht verloren und wir haben eine elegante Möglichkeit, auch mehrere unabhängige Ajax-Update-Komponenten in einer Ansicht zu definieren.
Um die Komponente einzusetzen, muss einfach nur das Tag <mc:ajaxUpdate/> in die Deklaration eingefügt werden - vorausgesetzt das Präfix mc ist entsprechend definiert.

7.4.2 Die Kompositkomponente mc:ajaxPoll

Für manche Anwendungsfälle ist es notwendig, Bereiche einer Seite in periodischen Abständen zu aktualisieren. Klassische Beispiele dafür sind Seiten mit Börsenkursen, Auktionen oder aktuellen Sportergebnissen. JSF stellt diese Funktionalität zwar nicht direkt zur Verfügung, sie lässt sich aber mit wenigen Zeilen JavaScript-Code in einer Kompositkomponente realisieren. Erneut erweist sich die JavaScript-API als äußerst nützlich.
Im Implementierungsteil werden zuerst die benötigten Ressourcen geladen. Dazu zählen die von JSF definierte JavaScript-Bibliothek jsf.js und das Skript ajaxPoll.js mit der notwendigen Funktionalität für die Komponente. Anschließend wird über einen Aufruf der in ajaxPoll.js definierten Funktion startAjaxPoll() das Polling gestartet. Als Parameter bekommt diese Funktion die ID des div -Elements, das über cc:insertChildren mit benutzerdefiniertem Inhalt gefüllt wird, und das Intervall. Beachten Sie bei der ID einmal mehr den Zugriff auf #{cc.clientId} , um die korrekte Client-ID zu erhalten. Listing Kompositkomponente ajaxPoll zeigt die Deklaration.
<cc:interface>
  <cc:attribute name="interval" required="true"/>
</cc:interface>
<cc:implementation>
  <h:outputScript name="jsf.js" library="javax.faces"
      target="head"/>
  <h:outputScript name="ajaxPoll.js"
      library="mygourmet" target="head"/>
  <script type="text/javascript">
  startAjaxPoll('#{cc.clientId}',#{cc.attrs.interval});
  </script>
  <div id="#{cc.clientId}">
    <cc:insertChildren/>
  </div>
</cc:implementation>
Die gesamte Ajax-Funktionalität der Komponente steckt in der Skript-Ressource ajaxPoll.js . Der Code ist in Listing JavaScript für Kompositkomponente ajaxPoll zu sehen.
function processPollEvent(interval) {
  return function(data) {
    if (data.status == 'success') {
      startAjaxPoll(data.source.id, interval);
    }
  };
}
function poll(clientId, interval) {
  var element = document.getElementById(clientId);
  element.mgPoll = true;
  jsf.ajax.request(element, null, {render: clientId,
      onevent: processPollEvent(interval)});
}
function startAjaxPoll(clientId, interval) {
  setTimeout("poll('"+clientId+"', "+interval+")", interval);
}
Als Einstiegspunkt dient die Funktion startAjaxPoll() , die als Parameter die ID des zu aktualisierenden DOM-Elements und das Intervall zwischen den Ajax-Anfragen bekommt. Die Ajax-Anfrage selbst wird in der Funktion poll() abgesetzt, die in startAjaxPoll() über die JavaScript-Funktion setTimeout() nach den im Intervall angegebenen Millisekunden aufgerufen wird. Die Funktion poll() sucht zuerst das DOM-Element für die übergebene Client-ID und schickt dann die Abfrage mit jsf.ajax.request() ab. Der Clou an der Sache ist, dass über eine Callback-Funktion nach erfolgreichem Abschluss der Anfrage über startAjaxPoll() der gesamte Prozess nochmals gestartet wird. Dadurch entsteht der Polling-Effekt und es ist gewährleistet, dass im Fall eines Fehlers keine weiteren Anfragen an den Server abgeschickt werden.
Einen kleinen Schönheitsfehler hat die Komponente noch. Wenn gleichzeitig ajaxStatus zum Einsatz kommt, wird bei jedem Update die Statusmeldung angezeigt - ein Verhalten, das nicht immer angebracht ist. Doch auch dafür gibt es eine sehr einfache Lösung. Wie Sie vielleicht schon bemerkt haben, wird in poll() dynamisch die Eigenschaft mgPoll auf das DOM-Element gesetzt. Diese Eigenschaft kann dann in der Callback-Funktion von ajaxStatus mit data.source.mgPoll abgefragt werden. Ist sie gesetzt, wird die Meldung nicht eingeblendet.
Um die Komponente einzusetzen, muss nur das Tag mc:ajaxPoll mit dem gewünschten Inhalt in die Deklaration eingefügt werden - vorausgesetzt das Präfix mc ist entsprechend definiert. Hier ein Beispiel, mit dem eine Art Uhr in die Ansicht eingebaut wird:
<mc:ajaxPoll interval="950">#{customerBean.time}</mc:ajaxPoll>

7.5 MyGourmet 14: Ajax

MyGourmet 14 ist vom Funktionsumfang her identisch mit MyGourmet 13 . Der große Unterschied liegt in der Integration von Ajax, wodurch an einigen Stellen ein komplettes Neuladen der Ansicht vermieden wird. Die Anwendung wirkt dadurch insgesamt flüssiger. Abbildung MyGourmet 14 mit Ajax-Status zeigt einen Screenshot der Ansicht showCustomer.xhtml mit aktivierter Ajax-Status-Meldung.
Abbildung:MyGourmet 14 mit Ajax-Status
Damit Benutzer immer über den Status einer Ajax-Anfrage informiert sind, fügen wir die Kompositkomponente ajaxStatus direkt im Template customerTemplate.xhtml in den Header ein. Listing Die Komponente ajaxStatus im Einsatz zeigt den aktualisierten Header-Bereich.
<ui:define name="header">
  <h:graphicImage name="images/logo.png"/>
  <h1>#{msgs.title_main}</h1>
  <mc:ajaxStatus style="float:right; width: 100px;"/>
</ui:define>
Nachdem die Ajax-Anfragen in MyGourmet recht zügig bearbeitet werden, blitzt die Statusmeldung nur kurz auf. Wenn Sie sie in ihrer vollen Pracht betrachten wollen, können Sie zum Beispiel mit Firebug einen Breakpoint in das Skript setzen.
Die Klasse DebugPhaseListener hat ebenfalls eine kleine Erweiterung bekommen. Die Logmeldungen zu Beginn und am Ende jeder Phase geben jetzt Auskunft darüber, ob es sich bei der Anfrage um eine Ajax-Anfrage handelt oder nicht. Um das festzustellen, rufen wir die Methode isAjaxRequest() des Partial-View-Contexts auf, der über den Faces-Context erreichbar ist. Listing Erweiterte Version des Debug-Phase-Listeners mit Ajax-Support zeigt den Sourcecode.
public class DebugPhaseListener implements PhaseListener {
  private static Log log = LogFactory.getLog(
      DebugPhaseListener.class);

  public void afterPhase(PhaseEvent ev) {
    String ajax = getAjaxText(ev.getFacesContext());
    PhaseId phaseId = ev.getPhaseId();
    log.debug("After phase: " + phaseId + ajax);
  }
  public void beforePhase(PhaseEvent ev) {
    String ajax = getAjaxText(ev.getFacesContext());
    PhaseId phaseId = ev.getPhaseId();
    log.debug("Before phase: " + phaseId + ajax);
  }
  public PhaseId getPhaseId() {
    return PhaseId.ANY_PHASE;
  }
  private String getAjaxText(FacesContext ctx) {
    return ctx.getPartialViewContext()
        .isAjaxRequest() ? " (Ajax)" : "";
  }
}
Die restlichen Änderungen umfassen nur die im Laufe des Kapitels mit Ajax erweiterten Ansichten der Anwendung. Dazu zählt neben dem dynamischen Ein- und Ausblenden der Kreditkartendaten in editCustomer.xhtml auch das collapsiblePanel mit den Adressen in der Seite showCustomer.xhtml . Dort gibt es ja sogar die doppelte Ajax-Funktionalität: In der Kompositkomponente selbst und in der darin eingebetteten Tabelle zum Löschen einer Adresse.
In Abschnitt [Sektion:  Ein erstes Beispiel mit f:ajax] haben wir bereits eine Lösung zum dynamischen Ein- und Ausblenden der Kreditkartendaten mittels f:ajax gezeigt. Die dort präsentierte Umsetzung hat allerdings noch einen kleinen Schönheitsfehler: Wenn im eingeblendeten Zustand die Kreditkartendaten geändert, ausgeblendet und wieder eingeblendet werden, gehen die Änderungen verloren. Ein genauerer Blick auf das eingesetzte f:ajax -Tag zeigt den Grund. Nachdem wir das Attribut execute nicht angegeben haben (und somit der Standardwert @this zum Einsatz kommt), bearbeitet JSF bei der partiellen Ausführung des Lebenszyklus nur das Auswahlfeld. Die geänderten Daten kommen nie am Server an und werden durch das erneute Rendern im Browser mit den alten Werten überschrieben.

7.6 Werkzeuge für den Ajax-Entwickler

Ajax ist durch die Vielzahl an verwendeten Basistechnologien nicht gerade einfach einzusetzen. Will man Ajax-Anwendungen bauen, ist eine Unterstützung durch geeignete Entwicklungswerkzeuge unabdingbar. Die folgende Aufzählung von Tools erhebt keinen Anspruch auf Vollständigkeit. Wir wollen Ihnen eine Reihe von Werkzeugen präsentieren, die sich in der täglichen Arbeit mit JSF-Projekten bewährt haben.

7.6.1 Firebug

FirebugFirebug ist unter http://getfirebug.com und als Add-on erhältlich.: ist eine Erweiterung von Firefox , die eine ganze Reihe sehr nützlicher Werkzeuge für Webentwickler direkt in den Browser integriert. Firebug ist frei verfügbar und erweist sich in vielen Situationen als unbezahlbar. Besonders dann, wenn es darum geht, den DOM-Baum zu analysieren oder JavaScript-Code zu debuggen. Hier eine Liste der wichtigsten Features:
Der Funktionsumfang und die Benutzerfreundlichkeit verbessern sich mit jeder neuen Version. Mittlerweile gibt es sogar eine Liste von sehr nützlichen Erweiterungen für Firebug selbst. Dazu zählt zum Beispiel YSlow zur Überprüfung von HTML-Seiten auf Performance-Probleme.

7.6.1.1 Analyse des DOM-Baums

Versucht man die Aktualisierung durch die Ajax-Antwort mit einem Editor nachzuvollziehen, wird keine Änderung des HTML-Codes sichtbar sein. Die Webseite wurde nicht komplett neu geladen, sondern nur der DOM-Baum aktualisiert. Diesen DOM-Baum rendert der Browser und stellt ihn grafisch dar. Die Veränderung kann nur durch die Visualisierung des Baums selbst sichtbar gemacht werden. Firebug bietet die visuelle Darstellung des Baums der HTML-Elemente in Form einer Verzeichnisstruktur. Abbildung Firebug mit aktualisierter Komponente zeigt die Ansicht showCustomer.xhtml mit eingeklapptem Adressen-Panel und aktiviertem Firebug .
Abbildung:Firebug mit aktualisierter Komponente
Es ist auch möglich, Knoten über deren ID, Tag-Name oder Attribute zu suchen, diese im Browser herausheben zu lassen oder deren Eigenschaften zu ändern. Firebug zeigt aber nicht nur den aktuellen HTML-Code, sondern hebt sogar Elemente farblich hervor, die sich gerade geändert haben.
Im unteren Panel in Abbildung Firebug mit aktualisierter Komponente ist der mit der Webseite synchron gehaltene DOM-Baum zu sehen. Das hervorgehobene Element ist der div -Block des Panels, in dem die Liste der Adressen angezeigt wird. Bei einer initialen Anfrage auf die Seite ist das Panel immer ausgeklappt. Der in der Abbildung erkennbare, eingeklappte Zustand ist erst als Reaktion auf die Ajax-Anfrage nach dem Klick auf das Icon eingetreten.
Sobald man in der Baumansicht ein Element anwählt, wird dieses im Browser farblich hervorgehoben. Es ist auch möglich, direkt aus dem Browserfenster ein Element anzeigen zu lassen. Auf der rechten Seite sind zusätzliche Informationen über die Knoten, wie Name, ID oder die Eigenschaften, abrufbar. Es gibt auch die Möglichkeit, in eine JavaScript-Objektansicht umzuschalten. Wie in Abbildung Ansicht mit Attributen und Methoden in Firebug zu sehen, kann in sämtliche Methoden und Attribute eines JavaScript-Objektes Einsicht genommen werden.
Abbildung:Ansicht mit Attributen und Methoden in Firebug
Auch wenn Sie den Internet Explorer einsetzen, müssen Sie nicht auf einen DOM-Inspektor verzichten. Ab Version 8 ist ein entsprechendes Werkzeug sogar bereits im Standardlieferumfang enthalten. Für ältere Versionen empfiehlt sich die Internet Explorer Developer ToolbarDie Internet Explorer Developer Toolbar ist unter der Adresse erhältlich.: .

7.6.1.2 JavaScript-Debugging

Ein unersetzliches Werkzeug für Ajax-Entwickler ist ein JavaScript-Debugger, wie ihn zum Beispiel Firebug für Firefox bietet. Mit diesem kann zur Laufzeit JavaScript-Code der Webseite oder der Ajax-Bibliothek in einzelnen Schritten durchlaufen werden. So wird komplexer Code leichter verständlich, Objekte, ihre Daten und Methoden erkennbar und fehlerhaftes Verhalten einfach ermittelbar. Wie in herkömmlichen Debuggern ist es möglich, Breakpoints zu setzen und sich an dieser Stelle der Ajax-Applikation den Zustand von Variablen und Objekten anzusehen und Funktionen aufzurufen. Ein Praxisbeispiel ist in Abbildung JavaScript-Debugging in Firebug zu sehen.
Abbildung:JavaScript-Debugging in Firebug
Hier wurde ein Breakpoint in das Skript inputSpinner.js gesetzt. Sobald der Benutzer auf eines der Bilder zum Erhöhen oder Reduzieren des Werts klickt, springt das Fenster des Debuggers auf und die Abarbeitung des Codes hält genau an dieser Stelle an. Im Fenster Überwachen kann jeder erdenkliche JavaScript-Befehl eingegeben werden. Hier zum Beispiel der Code, um das Input-Spinner-Eingabefeld anzuzeigen: <
document.getElementById('form:stars')
Der Internet Explorer verfügt ab Version 8 bereits im Standardlieferumfang über ein recht brauchbares Entwicklerwerkzeug mit DOM-Browser und JavaScript-Debugger. Für ältere Versionen ist der Microsoft Script DebuggerMicrosoft Script Debugger ist unter http://www.microsoft.com/downloads erhältlich.: als Freeware erhältlich.

7.6.2 HTTP-Debugger

Manchmal kann es recht nützlich sein, sich den Protokollablauf ein wenig näher anzusehen. Mit einem HTTP-Debugger ist es möglich, den kompletten HTTP-Verkehr mitzuschneiden. Eines der bekanntesten Werkzeuge in diesem Bereich ist der frei verfügbare HTTP-Debugger Fiddler (Windows) Fiddler ist unter http://fiddler2.com erhältlich.: . Die Zusammenarbeit mit dem Browser wird über das Programm selbst geregelt - es stellt einen lokalen Proxy dar. In muss der Proxy unter den Verbindungseinstellungen mit den Daten 127.0.0.1 und Port 8888 eingetragen werden. Im Internet Explorer sind keine weiteren Einstellungen zu treffen, denn die Zwischenstelle wird automatisch erkannt. Fiddler kann so konfiguriert werden, dass das Programm die Abarbeitung vor einer HTTP-Anfrage oder nach einer HTTP-Antwort - ähnlich einem gewöhnlichen Debugger - anhält und so der aktuelle Stand der Interaktion zu sehen ist. Dieser wird im linken Fenster von Fiddler protokolliert, wie es Abbildung Der HTTP-Debugger Fiddler protokolliert den HTTP-Verkehr zeigt. In Version 2 ist es sogar möglich, HTTPS-Verbindungen zu debuggen.
Abbildung:Der HTTP-Debugger Fiddler protokolliert den HTTP-Verkehr
Die angezeigten Daten sind der Stand auf eine Ajax-Anfrage nach der HTTP-Antwort. Im rechten oberen Fenster wird die übliche Protokollinformation der vorhergehenden Anfrage angezeigt. In unserem Beispiel handelt es sich um eine POST-Anfrage mit der Protokollversion 1.1 in einer bestimmten HTTP-Sitzung. Das Fenster rechts unten zeigt die Nutzdaten der Antwort an. Zu sehen ist die Liste, die vom XML-HttpRequest -Objekt aufgenommen und in weiterer Folge in den DOM-Baum eingefügt wird.
Für Firefox bietet Firebug mittlerweile auch eine sehr brauchbare Übersicht aller abgesetzten HTTP-Requests und ermöglicht das einfache Umschalten zwischen "vollen" und Ajax-Requests.

7.6.3 Web Developer Toolbar

Ein für den Webentwickler unersetzliches Werkzeug für Firefox und Chrome ist das Add-on Web DeveloperWeb Developer ist als Add-on zu Firefox und Chrome und unter der Adresse http://chrispederick.com/work/web-developer/ erhältlich.: . Grob umrissen bietet dieses Add-on folgende Funktionen an:
Die wichtigste Funktion der Erweiterung ist die Möglichkeit der Editierung von CSS und die sofortige Anzeige der Änderungen. Das Schreiben und mühsame ständige Umschalten in den Browser kann so vermieden werden. Der Entwickler kann sofort mitverfolgen, welche Auswirkungen einzelne CSS-Befehle haben.