Fachvortrag: JSF

Aus Nettundfroh
Wechseln zu: Navigation, Suche

Inhaltsverzeichnis

Quellen

  1. Ziemer
  2. Ziemers
  3. de.wikipedia
  4. en.wikipedia
  5. JSF 2.0 Tutorial
  6. JSFAtWork

Einleitung

Was sind JSF (kurz)

JavaServer Faces ist Java-Standard für ein UI-Komponenten-Framework und Teil von Java EE 6 (JSF 2.0).

JSF umfasst lediglich View-Model-Definitionen.

Die folgenden Frameworks implementieren JavaServer Faces:

  • Mojarra, die Referenzimplementierung von JavaServer Faces im Rahmen des GlassFish Java Anwendungsservers
  • Apache MyFaces und MyFaces Trinidad von der Apache Software Foundation
  • Oracle ADF, ein kommerzielles Java-Framework für Java-Enterprise-Applikationen (Vorläufer von Apache MyFaces Trinidad)

Folgende Funktionalitäten sind in JSF definiert:

  • Eine Tag-Bibliothek für die Darstellung von UI-Komponenten.
  • Eine Tag-Bibliothek für die Abbildung von Eventhandlern, Feldvalidierung und weiteren Aktionen.
  • Vordefinierte UI-Komponenten.
  • Ein Set an Handlern für Eventbearbeitung, Navigation und Feldprüfung.
  • Die Möglichkeit eigene Komponenten zu verwenden und eigene Renderer oder Javascriptanbindung zu definieren

Geschichte

  • JSF 2.0 (2009-06-28) — Hauptneuausgabe der Spezifikation unter Entwicklung. Plante, mit übereinzustimmen Java EE 6
  • JSF 1.2 (2006-05-11) — (DEPRECATED) many improvements to core systems and APIs
  • JSF 1.1 (2004-05-27) — Wanze Verlegenheit Freigabe. Es gab keine Spezifikation, oder HTML renderkit ändert
  • JSF 1.0 (2004-03-11) — Die Ausgangsfreigabe der JSF Spezifikation

Technologie

Standards

JavaServer Faces umfasst :

  • eine Java-API zur Kontrolle von Bedienelementen der Benutzerschnittstelle. Dies schließt auch die Behandlung von Ereignissen, die Validierung der Eingabe, Navigation und Unterstützung für Internationalisierung mit ein.


  • Eine Tag-Bibliothek zur Einbindung der JSF-Oberflächenkomponenten in JavaServer Pages oder in einem Facelet,
Tag-Name Beschreibung
<f:view> Erzeugt Instanz der Klasse javax.faces.component.UIViewRoot. Alle enthaltenen Komponenten werden Child-Komponenten der View.
<h:form> Bindet Eingabekomponenten in ein Formular. Sendet POST-Request per HTTP.
<h:panelGrid> In HTML <table>-Tag. Anzahl der Spalten bestimmt das columns-Attribut
<f:facet> Definiert eine Facette als Kindelement eines Containers (z. B. Überschrift für eine Tabelle)
<h:outputText> Normaler HTML-Text
<h:outputLabel> In HTML <label>-Tag. Kennzeichnung eines Eingabefeldes
<h:panelGroup> Container, der mehrere JSF-Tags zu einem Tag zusammenfügt
<h:inputText> In HTML <input>-Tag mit type="text", value-Attribut bezieht sich auf die Komponenten (z. B. EL-Ausdruck)
<h:inputSecret> In HTML <input>-Tag mit type="password"
<h:commandButton> Button in HTML. <input>-Tag mit type-Attribut (submit, reset, image). Standardmäßig: submit. Sendet Formular ab und löst ActionEvent aus (über Bean-Methode).
<h:message> Gibt die erste Fehlermeldung für die im for-Attribut angegebenen Komponenten aus. ErrorStyle-Attribut kann als CSS-Fehler-Darstellung genutzt werden.


  • Unified-EL: ermöglicht Komponenten der Benutzerschnittstelle und der Geschäftsdaten dahinter sehr dynamisch zu verbinden.


Kurze Überblick über die Möglichkeiten, die die Unified-EL bietet.

   * value="#{user.username}"
     Bindet einen Komponentenwert/ein Komponentenattribut an das Attribut username der Managed-Bean mit dem Namen user . 
   * rendered="#{user.username != null}"
     Bindet einen booleschen Wert ( true oder false ) an das Attribut der Komponente. 
   * value="#{bill.sum * 13,7603}"
     Wenn der Wert einer h:outputText -Komponente mit diesem Ausdruck versehen ist, wird in der Komponente immer der aktuelle   
     Wert der Eigenschaft sum der Managed-Bean bill stehen, multipliziert mit 13,7603. 
   * style="#{grid.displayed ? 'display:inline;' : 'display:none;'}"
     Hier wird das Attribut style einer Komponente auf entweder display:inline; oder display:none; gesetzt, ein sehr häufig   
     verwendeter Trick, um Bereiche einer Seite am Client für gewisse Attributwerte aus- oder einzublenden.
   * value=" Hallo Benutzer #{user.username}"
     Auch diese Kombination ist möglich - eine Zeichenkette kann mit Unified-EL -Ausdrücken durchmischt werden. Auf diese Weise   
     lassen sich sehr einfach dynamische Ausdrücke erzeugen. 
   * action="#{user.storeUser}"
     Hier wird die Aktion storeUser über eine Method-Expression aufgerufen. D
   * value="#{mapBean['index']}"
     Für den Zugriff auf Werte in Objekten, die das Interface Map implementieren, kann der Schlüsselnamen in eckigen Klammern 
     angegeben werden. Diese Notation ist prinzipiell äquivalent zur Punktnotation, es kann also auch mit der Punktnotation auf  
     die Inhalte einer Map referenziert werden. 
   * value="#{mapBean[user.username]}"
     Hier die Auflösung der Aufgabe der vorigen Runde: Value-Expressions kann man auch schachteln und dabei lässt sich dann der 
     Schlüssel für den Zugriff auf den Wert der Map oder der Eigenschaftsname für den Zugriff auf die Bean-Eigenschaft über eine 
     Value-Expression angeben.
   * value="#{listBean[5]}"
     Ein letztes Beispiel: Hier referenziert der Wert der Komponente auf den sechsten Eintrag einer Liste. Auch dieser Index kann 
     wieder eine Value-Expression sein, so wie im obigen Beispiel.

Java EE 6 bringt eine neue Version der Unified-EL mit einigen lang erwarteten Neuerungen. Mit der neuen Version ist es endlich möglich, in EL-Ausdrücken Methoden mit Parametern zu verwenden. Bislang war das nur für statische Methoden über EL-Funktionen möglich.

Folgender Code zeigt eine Steuerkomponente zum Löschen einer Adresse aus einer Liste:

<h:commandLink value="Delete"

   action="#{customerBean.deleteAddress(address)}"/>

Als weitere Neuerung sind Value-Expressions nicht mehr auf Eigenschaften von Beans beschränkt. Die neue Unified-EL erlaubt den Aufruf einer beliebigen Methode, deren Rückgabewert den Wert der Value-Expression bildet. Damit sind folgende Unified-EL -Ausdrücke möglich:

   * value="{}#{bean.list.size()}"{}
     Mit diesem Ausdruck ist es endlich möglich, die Anzahl der Elemente einer Liste ohne Umwege auszulesen.
   * value="{}#{bean.text.replaceAll(':', '_')}"{}
     Dieser Ausdruck ruft auf der Eigenschaft text vom Typ String die Methode replaceAll() auf, um alle Doppelpunkte durch 
     Unterstriche zu ersetzen, und liefert das Ergebnis zurück.
   * value="{}#{bean.findOrders(otherBean.customer)}"{}
     Der Wert dieses Ausdrucks wird über einen Aufruf der Methode findOrders() auf der Bean bean bestimmt. Als Parameter kommt 
     dabei die Eigenschaft customer der Bean otherBean zum Einsatz - auch das ist ohne Probleme möglich.
   * value="{}#{bean.getName()}"{}
     Diese Value-Expression bindet den Rückgabewert der Methode getName() an die Komponente. Im Gegensatz zum Ausdruck 
     #{bean.name} kann #{bean.getName()} nur gelesen werden - auch wenn die Methode setName() existiert - und ist daher nicht für 
     Eingabefelder geeignet.

Ähnlichkeit mit JSP

JSF Lebenszyklus

In Abhängigkeit von der Request- bzw. Responseart werden verschiedene Aktionen von Framework ausgeführt. Dabei werden bei der Arbeit mit JSF generell je zwei Arten von Request (Anfrage) und Response (Antwort) unterschieden:

• Faces-Request: die Abfrage einer JSF-Seite

• Non-Faces-Request: alle anderen Anfragen (JSP, statisches HTML etc.)

• Faces-Response: Antwort mit einer JSF-Seite

• Non-Faces-Response: alle anderen Antworten (JSP, statisches HTML etc.)


Die Requests und Responses können in beliebiger Kombination auftreten, so kann es vorkommen, dass von einer reinen HTML-Seite (Non-Faces-Request) zu einer Faces-Seite (Faces-Response) verwiesen wird.

Der Standardfall bei der Arbeit mit JSF ist der Faces-Request gefolgt von einer Faces-Response. Dieses wird auch der Standard Lebenszyklus eines Request genannt und soll hier nun genauer betrachtet werden, da an diesem Ablauf viele Eigenschaften von JSF erklärt werden können.


Datei:Beispiel.jpg


1.Restore View

Im Falle eines Non.Faces Requestes wird in dieser Phase zunächst ein leeres View-Objekt erzeugt, in dem ein leerer Komponentenbaum angelegt und im FacesContext einhängt wird. Danach kann sofort zur Render Response-Phase gesprungen, da keine Verarbeitung von übergebene Werten etc. nötig ist.

Sollte bereits ein Komponentenbaum im FacesContext existieren, so wird dieses View-Objekt geladen und mit Validatoren, Konvertern und Listenern verknüpft.


2.Apply Request Values

In dieser Phase werden die übertragenen Werten des Requests (also die Daten des abgeschickten Formulars) in den Komponentenbaum übernommen, also in den entsprechenden Komponenten gesetzt.

ActionEvents werden hier generiert, z.B, das Drücken des Buttons durch den der Request erzeugt wurde.

Die Konvertierung und Validierung wird dann in der nächste Phase vorgenommen, allerdings kann diese Phase auch vorgezogen werden: Mit dem Setzen des immediate -Attributs wird die Komponente "angewiesen", die Konvertierung und Validierung bereits in Phase 2 durchzuführen.

(Der Standardwert für immediate ist false , was einem normalen Ablauf des Lebenszyklus entspricht. Wird der Wert auf true gesetzt, ändert sich das Verhalten der Komponenten.)


3.Process Validation

Anhand von Konvertern (aber auch Renderern) werden die gespeicherten Werte der vorherigen Phase in die Zielformate (Modelldatentypen) überführt. Anschließend werden alle Werte der Komponenten mittels registrierter Validatoren überprüft.

Im Fehlerfall werden dabei üblicherweise komponentenbezogene Meldungen generiert und es wird mit der Render Response-Phase fortgefahren. ( Wodurch dieselbe Seite gerendert wird, da ihr Komponentenbaum noch FacesContext liegt)


4.Update Model Values

Wenn bis hier hin kein Fehler auftrat, so werden die überprüften Werte in das Modell übernommen. Dafür werden die Setter-Methoden aufgerufen, die notwendig sind, um das Modell mit den neuen Daten zu aktualisieren.

Dabei werden ValueChangeEvents generiert, falls sich ein Wert geändert hat. Nach dieser Phase werden die registrierten Listener über Wertänderungen informiert.


5.Invoke Application

Alle Ereignisse der Anwendungsebene werden verarbeitet. So wird z.B. die nachfolgende Seite ermittelt und ihr Komponentenbaum im Kontext abgelegt. Nach dieser Phase werden alle registrierten ActionListener benachrichtigt.


6.Render Response

Der im Kontext befindliche Komponentenbaum wird ausgegeben. Dazu wird die encode-Methode jeder Komponente ausgeführt.

Sollten Fehler in vorherigen Phasen aufgetreten sein, wird die ursprügliche Seite nicht mal dargestellt. Wird also die Phase Invoke Application übersprungen so erfolgt das Rendern derselben Seite.


Ergänzung : Process Events

Zum einen werden die für Events registrierte Listener benachrichtigt, als auch PhaseListener benachrichtigt. Letztere können den Ablauf beeinflusse oder nebenläufige Tätigkeiten angestoßen (z.B. Logging).

Generell wird bei schweren Fehlern die Verarbeitung abgebrochen (Response Complete) oder vorzeitig eine Ausgabe erzeugt (Render Response).

Komponenten-Frameworks

Komponentenframeworks erweiteren JSF-Implementationen.

  • ICEfaces, Opensource-Framework von ICEsoft; basierend auf dem Woodstock GUI-Komponentenframework.
  • RichFaces inkl. Ajax4jsf von Red Hat (ehemals Exadel)
  • PrimeFaces
  • Mojarra Scales
  • J4Fry
  • xulfaces
  • jQuery4jsf

Vorteile

  • komponentenbasiertes Entwicklungsmodell: geeignet für Rapiddevelopment sowohl für kleine als auch große Projekte
  • Convention over Configuration
  • Javascript- und Ajax-Generierung sowie Zustandsspeicherung beim Client
  • Renderer, Tag-Bibliothek und Javascript-Anbindung völlig frei definierbar
  • client- und serverseitige Eingabevalidierung

Nachteile - was JSF nicht kann

  • rein HTTP-orientiert
  • kein Authentifizierungsmechanismus

Voraussetzung für ein JSF-Projekt

  • Servlet-Container (Tomcat, Glassfish...)
  • JSF-Bibliotheken
  • eingebundenes JSF-Servlet in der web.xml
  • services-config.xml

faces-config.xml

Hier können ManagedBeans, ManagedProperties und Navigation-Cases definiert werden.

Ab JSF 2.0 kann diese Datei auch komplett weggelassen werden, wenn Annotationen verwendet werden.

Im Sinne von Convention over Configuration können Annotationsdeklarationen in dieser Datei auch überschrieben werden (ähnliches Verhalten wie in persistence.xml)

<?xml version="1.0"?>
<faces-config ... version="2.0">
...
</faces-config>

Explizite Deklaration von ManagedBeans und ManagedProperties:

<managed-bean>
    <managed-bean-name>meinBeanName</managed-bean-name>
    <managed-bean-class>meinPaket.MeineKlasse</managed-bean-class>
    <managed bean scope>request</managed bean scope>
    <managed-property>
        <property-name>user</property-name>
        <value>#{userManager}</value>
    </managed-property>
</managed-bean>

Explizite Navigationsregeln:

<navigation-rule>
    <from-view-id>/some-start-page.xhtml</from-view-id>
    <navigation-case>
        <from-outcome>return-condition-1</from-outcome>
        <to-view-id>result-page-1.xhtml</to-view-id>
    </navigation-case>
    <!-- weitere Einträge zu anderen Bedingungen -->
</navigation-rule>
<from-action>accepted</from-action>
ist auch anstelle des from-outcome-Tags möglich.
<if>#{!user.returnVisitor}</if>
kann auch über dem <to-view-id>-Tag verwendet werden

return-condition-1 könnte dabei der zurückgegebene Wert einer, im action-Attribut eines h:commandButtons, aufgerufenen Funktion sein.

Dadurch kann auch Seitenverzweigung auch nachträglich per Konfiguration manipuliert werden.

In der faces-config.xml kann folgendes definiert werden:
1. ManagedBeans
2. ManagedProperties
3. Navigationsregeln
4. Phase-Events

Das verwenden der faces-config. xml ist seit JSF 2.0 keine pflicht mehr.
Die ManagedBeans und ManagedProperties können auch mit Anotations definiert werden.

ManagedBeans

@ManagedBean(name="yourClass")
public class YourClass implements Serializable {
...

Lässt Lebenszyklus der Objekte der Klasse vom JSF-Framework verwalten.

In einer JSF-Datei per Expression Language-Ausdruck #{yourClass.yourAttributeWithGetter} zu erreichen.

Ab JSF 2 ist kein Eintrag in der faces-config.xml für jede Bean nötig, wenn annotiert wird.

Das name-Attribut ist optional. Defaultwert ist der Name der Klasse mit einem kleinen Buchstaben beginnend.

Statischer Zugriff

Es kann von überall zu jeder Zeit auf die aktuelle Instanz vom FacesContext zugegriffen werden:

javax.faces.context.FacesContext.getCurrentInstance()

Die normalen Gültigkeitsbereiche sowie der ServletContext kann ebenso über FacesContext erreicht werden:

ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.getRequest();
ec.getSession();
ec.getContext();
...

Gültigkeitsbereiche

@ApplicationScoped

  • eine einzige Beaninstanz über die gesamte Laufzeit des Servlet-Containers hinweg

Beispiel: UserListManager

@RequestScoped

  • pro Request wird eine neue Bean instantiiert

Beispiel: NavigationManager

@SessionScoped

  • Bean-Instanz wird in der Session gespeichert

Beispiel: UserManager

@ViewScoped

  • Solange der Benutzer auf der selben Seite ist, wird die selbe Bean-Instanz genutzt
  • besonders nützlich für Ajax(-Polling)

Beispiel: ChatManager

@NoneScoped

  • Bean wird in keinen Gültigkeitsbereich gesetzt
  • nützlich für Beans, die nur als ManagedProperties von anderen Beans, die sich in einem bestimmten Gültigkeitsbereich befinden, referenziert werden

@CustomScoped(value="#{obj}")

  • Eine solche Bean wird in obj gespeichert, so dass der Lebenzyklus vom Programmierer kontrolliert werden kann.

@ManagedProperty

Mittels Dependency-Injection können Attribute von einer Bean mit einer Instanz einer anderen Bean, die sich auch in einem anderen Gültigkeitsbereich befinden kann, belegt werden.

Der Wert des Value-Attributs ist ein EL-Ausdruck, durch dessen Inhalt sich die Zuweisung definiert.

Im Gegensatz zur ManagedBean-Annotation, die auch ohne jegliche Attribute stehen kann, ist bei ManagedProperty immer das value-Attribut erforderlich.

@ManagedProperty(value="#{userManager}")
private UserManager userManager;

Dieses Beispiel ist dem ChatManager entnommen und zeigt, wie dieser (ViewScoped) eine Instanz vom UserManager erhält (SessionScoped).


Dependency-Injection heißt in der Praxis, dass Objekte der betroffenen Klassen nicht mehr selbst instantiiert werden müssen, sondern es bereits einen Mechanismus gibt, der dies erledigt und dabei auch die Lebenszeit, den Gültigkeitsbereich verwaltet. In JSF 2 geht das so weit, dass ManagedBeans nicht mehr zwangsläufig in der faces-config.xml definiert werden müssen, sondern bei erstmaligem Zugriff in einem JSF-Dokument einfach da sind.

 ManagedPropertys können an Variablen einer ManagedBean vergeben werden.
 
 Dadurch wird mittels Dependency-Injection der Variable ein wert einer anderen Bean übergeben.
 
 @ManagedProperty(value="#{userManager}")
 private UserManager userManager;
 
 - Es ist immer das value-Attribut nötig.
 - 
 


ManagedBean-Lifecycle-Annotationen

Sei eine Klasse:

@ManagedBean
public class UserManager {
@ManagedProperty(value="#{userListManager}")
private UserListManager list;
@PostConstruct

Im Konstruktor einer Bean kann kein Objekt verwendet werden, das durch Dependency-Injection eingesetzt wird, da dies erst nach der Instantiierung geschehen kann.

Daher lässt sich so eine Methode deklarieren, von der nachträglich die Initialisierung vorgenommen wird.

Bsp:

@PostContruct
public void init() {
    list.add(this);
}
@PreDestroy

Eine Art Destruktor. Bsp: Wenn eine Session gelöscht wird, sollte das entsprechende User-Objekt aus der Liste der aktiven Benutzer genommen werden. Dies kann in einer solchen Methode geschehen. Bsp:

@PreDestroy
public void remove() {
    list.remove(this);
}
Guter Ersatz für HttpSessionListener und HttpSessionBindingListener.

Expression Language

Beispiel-Bean:

@ManagedBean
@SessionScoped
public class UserManager implements Serializable {
    static private long count = 0;
    private User currentUser;
    public User getCurrentUser() { return currentUser; }
    public long getLastId() {return count;}
    public List<User> listActiveUsers() { ...
}

Expression Language Beispiel:

#{userManager.getLastId() + 4} </br />
#{userManager.getLastId() > 7 ? 'größer 7' : 'kleiner 7'} <br />
Ich bin #{userManager.user.name}.<br />
aktive Benutzer: <br />
<ui:repeat var="user" value="#{userManager.listActiveUsers()}">
   #{user.name} <br />
</ui:repeat>

Könnte ausgeben:

8
kleiner 7
Ich bin Fietz.
aktive Benutzer:
Fietz
Igor
Giacomo
Max


  • Getter und Setter werden automatisch ausgeführt, wenn ein Attribut angesprochen wird
  • Methodenaufrufe sind ebenfalls möglich
  • neue EL-Variablen können erstellt werden (bsp: user)
  • Zugriff auf beliebige öffentliche Kindobjekte einer Bean möglich, auch wenn diese selbst keine Beans sind
  • Boolsche Ausdrücke und Berechnungen sind ebenfalls möglich
  • seit JSF 2 kann EL nicht nur in Tag-Attributen sondern auch im inneren von beliebigen Tags stehen

Komponenten / Views

Facelets

Ein Facelet ist ein XML/XHTML-Dokument, das auch JSF-Tags und Elemente der Unified-EL enthalten kann.

Es kann eine einzelne Seite, eine Komponente mit Argumenten sein, Positionen in einem Template füllen und direkt vom FacesServlet angesteuert werden oder einfach nur als Snippet in einem anderen Kontext eingebunden werden.

Snippets

Simples Snippet, das nur zur Einbindung geeignet ist, da es kein valides XHTML ausgibt:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html">

    <span>Es war einmal ein Beispiel namens </span>
    <h:outputText id="name" value="#{bsp.name}" />.

</ui:composition>

Einbindung:

<ui:include src="/WEB-INF/snippets/userList.xhtml" />

Templates

Ein Template enthält XHTML und <ui:insert name="positionName">-Tags, die Standardwerte enthalten können.

/WEB-INF/templates/default.xhtml:

<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html">

    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>
            '''<ui:insert name="title">DEFAULT TITLE</ui:insert>'''
        </title>
	<h:outputStylesheet name="css/layout.css"/>
	<h:outputScript name="js/jsfChat.js" />
    </h:head>

    <h:body>
		<div id="main">
			'''<ui:insert name="content">DEFAULT CONTENT</ui:insert>'''
		</div>
    </h:body>

</html>

Der Inhalt dieses Tags kann in einem anderen Facelet, welches das Template einbindet, von einem <ui:define name="positionName">-Tag überschrieben werden:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
				xmlns:h="http://java.sun.com/jsf/html"
				template="'''/WEB-INF/templates/default.xhtml'''">

    '''<ui:define name="title">Seitentitel 1</ui:define>'''

   '''</ui:define name="content">
        <p>Seiteninhalt 1</p>
    </ui:define>'''

</ui:composition>

Es ist zu empfehlen Templates an eine Stelle zu legen, wo sie vom Web aus nicht erreichbar sind.

"composite components"

  • in JSF 2.0 hinzugekommen
  • Ähnlichkeit mit JSP-Tag files
  • echte Komponente: kann eigene Attribute, Listener, Events, Facets etc. besitzen
  • Unterteilung von Eingabe- und Ausgabedaten (composite:interface, composite:implementation)
  • Das Prefix einer Komponente definiert sich durch den Unterordner von /WEB-INF/resources/, in dem sie liegen muss.
  • Die Komponenteninstanz selbst kann intern über das Objekt cc, das für "composite component" steht, erreicht werden
  • Argumente können übergeben und per cc.attrs auf sie zugegriffen werden

Beispiel einer eigenen Komponente:

<!DOCTYPE ...>
<html ...
	  xmlns:composite="http://java.sun.com/jsf/composite">
    <head><title>ich stehe hier nur um valide zu sein!</title></head>
    <body>
	<composite:interface>
		<composite:attribute name="message" required="false" default=""/>
	</composite:interface>
	<composite:implementation>
		#{cc.attrs.message}
	</composite:implementation>
    </body>
</html>

Einbindung der Komponente:

<!DOCTYPE ...>
<html ...
	  xmlns:myPackage="http://java.sun.com/jsf/composite/myPackage">
    <head><title>Willkommen auf meiner Komponentenseite</title></head>
    <body>
	<myPackage:myComponent message="Ich werde gleich zum Wert von cc.attrs.message!" />
    </body>
</html>

gibt eine HTML-Seite mit dem Inhalt "Ich werde gleich zum Wert von cc.attrs.message!" aus.

Ereignis-Modell

Mögliche Ereignisse

 Durch User ausgelöste Events
   - Value-Change-Events
   - Action-Events  
 Vom System ausgelöste Events
   - Phase-Events

User-Events

 Value-Change-Events werden ausgelöst, wenn sich der Wert einer Eingabekomponente ändert.
 (Event wird während der Abarbeitung des Lebenszyklus gefeuert)
 z.B. Checkbox
   
 Eingabekomponenten
 - CheckBox
 - TextField
 - ... 
 ----
 Action-Events werden von Steuerkomponenten ausgelöst, wenn sie aktiviert werden. 
 z.B. Buttons
 
 Steuerkomponenten
 - Command-Button
 - Command-Link
 Listener Anmelden  am Bsp. Value-Change-Event
 a) Method-Expression
     <h:selectBooleanCheckbox valueChangeListener="#{aManagedBean.aValueChangeEventHandler}"/>
 
     Normale Java-Methode in einer Managed-Bean "public void aValueChangeEventHandler(ValueChangeEvent e){...}"  
 
 b) Child-Element
 
    <h:selectBooleanCheckbox>
       <f:valueChangeListener type="org.jsfchat.AValueChangeHandler"/>
    </h:selectBooleanCheckbox>
 
    Eine solche Klasse muss das Interface javax.faces.event.ValueChangeListener implementiert.
 
    Es ist möglich, mehrere Listener an ein Element zu binden.
    Die Listener werden in der Reihenfolge ihrer Anmeldung abgearbeitet.
 Das immediate-Attribut
 Es bewirkt eine vorzeitige Behandlung des entsprechenden Events.
 So können z.B. weitere Formular-Elemente hinzugefügt werden, bevor die eingaben validiert werden.
 
 <h:selectBooleanCheckbox onclick="this.form.submit()"
   value="#{aManagedBean.aField}" immediate="true"
   valueChangeListener="#{aManagedBean.aValueChangeEventHandler}"/>


 Erweiterung zum Action-Event
 Betätigen einer Steuerkomponente bewirkt ein Senden der aktuellen Seite.
 
 Es gibt 2 arten zur Behandlung von Action-Events:
 - | ActionListener  | wird vor Action ausgeführt              | wird zur Behandlung der UI-Logik verwendet  |
 - | Action          | wird nach dem ActionListener ausgeführt | wird zur Behandlung der Fachlogik verwendet |
 Durch diese Trennung wird die Wartbarkeit deutlich erhöht.
 
 <h:commandButton action="#{aManagedBean.actionMethod}" 
                  actionListener="#{aManagedBean.actionListenerMethod}" />
 
 <h:commandButton action="#{aManagedBean.actionMethod}">
     <f:actionListener type="org.jsfchat.AActionListenerHandler"/>
 </h:commandButton>

Phase-Event

 Phase-Events werden vom System routinemäßig beim Abarbeiten des Lebenszyklus ausgelöst.
 Sie werden vor und nach jeder einzelnen Phase ausgelöst.
 Sie dienen Hauptsächlich zum Debuggen und Loggen.
 Sie werden in der faces-config.xml definiert.
 
 <lifecycle>
   <phase-listener>
       org.jsfchat.DebugPhaseListener
   </phase-listener>
 </lifecycle>
 
 Eine solche Klasse muss das Interface "javax.faces.event.PhaseListener" implementieren.

Warum "Unobtrusive Javascript" und was ist das?

"Unobtrusive Javascript" ist ein communitygeprägter Begriff und Grundsatz der Programmierung mit Javascript auf Webseiten.

Er beschreibt die Lösung folgender Probleme:

  • Darstellung einer Javascript-Webseite auch mit deaktiviertem Javascript und Lesbarkeit für Bots (Suchmaschinen-Bots führen kein JS aus)
  • "Standard"-Javascript und Browserweichen (inkonsistente Implementierung in verschiedenen Browsern / verschiedene Browserversionen)
  • klare Trennung von Inhalt/Struktur und Verhalten (im Sinne von MVC); Verhalten wird HTML-Elementen "übergestülpt"

Durch strikte Einhaltung des letzten Punktes ist auch der erste gelöst.


Bei der Implementierung von Javascript in Webseiten machen viele Anfänger den Fehler entscheidende steuernde oder inhaltliche Elemente mit JS zu erzeugen oder URLs von Links mit JS zu definieren und verlieren so einen großen Prozentsatz ihrer Seitenbesucher. Ähnlich erging es JSF < 2.0

<f:ajax> - JSF 2.0

Mit dem Tag f:ajax können alle Komponenten, die ClientBehaviorHolder implementieren (alle Standardkomponenten in JSF 2.0), mit Ajax-Verhalten ausgestattet werden.

Damit steht der Kompatibilität von Komponenten aus unterschiedlichen Quellen in Bezug auf Ajax nichts mehr im Wege.

Zwei Einsatzformen:

  • Komponenten können mit Ajax-Verhalten ausgerüstet werden, indem <f:ajax> als Kind-Tag eingefügt wird
  • Ein ganzer Bereich kann mit <f:ajax> ebenso auf Ajax umgestellt werden

Attribute

execute

Eine durch Leerzeichen separierte Liste der IDs jener Komponenten, die beim Bearbeiten der Ajax-Anfrage durch JSF im Lebenszyklus ausgeführt werden sollen.

Kann auch die Konstanten @this (das Element selbst), @form (das Formular des Elements), @all (alle Elemente) und @none (kein Element) beinhalten.

Wenn das Attribut als Value-Expressions gesetzt wird, muss der Wert vom Typ List<String> sein. Der Defaultwert ist @this.

render

Eine durch Leerzeichen separierte Liste der IDs jener Komponenten, die beim Bearbeiten der Ajax-Anfrage durch JSF im Lebenszyklus gerendert werden sollen.

Kann auch die Konstanten @this, @form ,@all und @none beinhalten.

Wenn das Attribut als Value-Expressions gesetzt wird, muss der Wert vom Typ List<String> sein.

Der Defaultwert ist @none.

event

Name des Ereignisses, das die Ajax-Anfrage auslöst.

Mögliche Werte sind valueChange für Eingabekomponenten, action für Steuerkomponenten und alle anderen HTML-Ereignisse - allerdings ohne das Präfix on.

Der Defaultwert dieses Attributs wird von der Komponente bestimmt.

onevent

Als Wert wird der Name einer Javascript-Methode erwartet.

onerror

Erlaubt das Registrieren einer JavaScript-Callback-Funktion für Fehler, die beim Bearbeiten der Ajax-Anfrage auftreten.

disabled

Das Ajax-Verhalten wird "abgeschaltet", wenn dieses Attribut auf true gesetzt ist.

Javascript-Request-API

jsf.ajax.addOnError(callback)

registriert eine Methode zur Behandlung von Fehlern

jsf.ajax.addOnEvent(callback)

registriert eine Methode zur Ereignisbehandlung
eine angemeldete Methode erhält als Parameter ein Ereignisobjekt mit den Attributen status, type, source, responseXML und responseText
Für die folgenden drei Zustände eines AJAX-Requests wird eine solche Methode in dieser Reihenfolge mit dem Wert von "status" aufgerufen:
begin - Request wird im Folgenden ausgeführt
complete - Antwort des Requests ist eingegangen
success - Views wurden mit den empfangenen Daten aktualisiert

jsf.ajax.request(source, event, options)

sendet einen asynchronen HttpXMLRequest zum Server

jsf.ajax.response(request, context)

empfängt / bearbeitet eine Antwort vom server

Beispiel

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html" 
	xmlns:f="http://java.sun.com/jsf/core">
	<h:form id="userNameForm">
		<h:outputText id="errorMessage"
					  styleClass="error"
					  value="#{userManager.errorMessage}" />
		<h:inputText id="userName" value="#{userManager.name}" />
		<h:commandButton value="speichern">
			<f:ajax execute="userName"
					render="errorMessage"
					onevent="yourHelper.displayProgress" />
		</h:commandButton>
	</h:form>
</ui:composition>

Die Klasse UserManager:

@ManagedBean
@SessionScoped
public class UserManager implements Serializable {

	private String name;
	private String errorMessage;

	public String getName() { return name; }

	@NotNull
	public void setName(String name) {
		name = name.trim();
		if (name.isEmpty() || nameExists(name))
			errorMessage = "Dieser Name ist nicht erlaubt!";
		else
			this.name = name;
	}
	
	public String getErrorMessage() {
		String res = errorMessage;
		errorMessage = "";
		return res;
	}

	private boolean nameExists(String name) { ...

}

Optional: Belegung des Javascript-Events:

var yourHelper = {

	displayProgress : function(data) {
		// zeigt/versteckt das "progress"-Element
		var elementStyle = document.getElementById("progress").style;
		if (data.status=="begin") {
			// wird zu Beginn jeder Anfrage ausgeführt
			elementStyle.display = "block";
		} else if (data.status=="success") {
			// wird ausgeführt nachdem das Ergebnis vom Server
			// eingetroffen ist und die Views aktualisiert wurden
			elementStyle.display = "none";
		}
	}
};

Das Element mit der ID "progress" könnte folgendermaßen aussehen und evtl. ein Bild enthalten, das den Ladezustand verdeutlicht:

<div id="progress">laden...</div>
Master Semester 2