Werte an Dialoge übergeben und von Dialogen zurückgeben
 
  Verwendung von tr:setActionListener
 
  Verwendung von RequestContext zur Verwendung von PPR im Source Code
 
  Umstellung h:commandButton auf tr:commandButton
 
  Umstellung der optionalen Kreditkartendetails
 
  Schaltfälchen für die Verarbeitung von Adresseingaben
 
  partialTriggers zur Aktualisierung des Formulars
 
  Liste der web.xml Kontextparameter für Trinidad
 
  Grundkonfiguration von Trinidad in der web.xml
 
  Ergebnisse eines Dialogs
 
  Dialoge abbrechen
 
  Button, um einen Dialog zu starten
 
  Button, um einen Dialog in einer bestimmten Fenstergröße zu starten
 
  Beispiel für tr:document
 
  Beispiel für Komponenten mit clientside validation
 
  Beispiel für einen konventionellen Validator
 
  Beispiel für einen clientside Validator - die JavaScript Seite
 
  Beispiel für einen clientside Validator - die Java Seite
 
  Beispiel für einen Ausschnitt aus trinidad-config.xml
 
  Beispiel für eine Navigationsregel und eine Action-Methode, um einen Dialog zu starten
 
  Ausschnitt des Templates - tr:form mit defaultCommand
 
  Ausschnitt aus der web.xml für die Verbindung von Trinidad und Facelets
 
  Ausschnitt aus der faces-config.xml für die Konfiguration von Trinidad
 
  Ausschnitt aus den Dependencies einer Maven pom.xml Datei
 
  ActionListener zum Hinzufügen der eingegebenen Adresse

11 Trinidad - Die Komponentenbibliothek

11.1 Trinidad - Ein Überblick

Trinidad wurde ursprünglich von Oracle unter dem Namen ADF Faces entwickelt und stellt eine umfangreiche Komponentenbibliothek für JSF dar. Später wurde die Komponentenbibliothek der Apache Software Foundation (ASF) gespendet. Genauer gesagt wurden ADF Faces zu einem Bestandteil des MyFaces Projekts. Unter dem neuen Namen Trinidad wurde die Komponentenbibliothek bis Mai 2007 im Incubator bearbeitet. Aktuell werden wie bei MyFaces Core zwei Releases geführt. Trinidad 1.0.x für MyFaces 1.1.x und Trinidad 1.2.x für MyFaces 1.2.x. Weitere Informationen bezüglich Kompatibilität von und rund um MyFaces werden auf http://myfaces.apache.org/compatibility.html bereitgestellt.

Warum Trinidad?
Einer der Vorteile von JSF ist, dass es mittlerweile eine Vielzahl an Komponentenbibliothek am Markt gibt. Neben kommerziellen Produkten gibt es auch mehrere Open-Source Projekte. MyFaces selbst umfasst Tomahawk, Tobago und Trinidad. Zusätzlich zu einer großen Anzahl an Komponenten mit umfangreichen Funktionalitäten bietet Trinidad durchgängige und fortschrittliche Konzepte. So unterstützen Trinidad-Komponenten unter anderem Ajax (auch Partial Page Rendering genannt), Skinning und clientseitige Validierung. Außerdem bietet Trinidad out-of-the-box Lösungen für die Request- vs. Session-Scope Thematik, effizienteres client-side state saving und transparente Unterstützung für Facelets. Nicht zu vergessen ist, dass Trinidad unter der Apache 2.0 License geführt wird und somit ein Open-Source Produkt ist, welches bedenkenlos in kommerziellen (Closed-Source) Projekten eingesetzt werden kann. Zusätzlich profitiert Trinidad von der professionellen Community, welche hinter dem MyFaces Projekt steht. Neben Dokumentation, Wikis, diversen Artikeln und Demo-Applikationen kann über die User-Mailing-Liste (users@myfaces.apache.org) auf fundierte Hilfe durch die MyFaces Community zurückgegriffen werden. Zusätzlich zu der Weiterentwicklung durch die MyFaces Community profitiert Trinidad zusätzlich durch die Unterstützung von Oracle. Zusätzlich stellt beispielsweise irian.at ( http://www.irian.at/ ) kompetente Unterstützung für den Einsatz von Trinidad in JSF-Projekten bereit. Detaillierte Informationen über die umfangreichen Angebote rund um Trinidad sind auf http://www.irian.at/adfFaces ersichtlich.

11.2 Installation und Konfiguration

Die Installation von Trinidad geht ähnlich leicht von der Hand, wie die von JSF selbst.

11.2.1 Dependencies

Der erste Schritt ist die Aufnahme der beiden Trinidad-Jar-Archive (trinidad-api und trinidad-impl) in den Classpath. Abbildung Ausschnitt aus den Dependencies einer Maven pom.xml Datei zeigt die erforderlichen Dependencies für die Konfiguration von Trinidad in Version 1.0.4 im pom.xml von Maven 2.
        <dependency>
            <groupId>org.apache.myfaces.trinidad</groupId>
            <artifactId>trinidad-api</artifactId>
            <version>1.0.4</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.myfaces.trinidad</groupId>
            <artifactId>trinidad-impl</artifactId>
            <version>1.0.4</version>
            <scope>compile</scope>
        </dependency>
Abbildung: Ausschnitt aus den Dependencies einer Maven pom.xml Datei
Alle weiteren Dependencies werden durch die Konfiguration von JSF abgedeckt. Anschließend ist es erforderlich Trinidad in der web.xml zu konfigurieren.

11.2.2 Konfiguration

11.2.2.1 web.xml - Grundkonfiguration

Um Trinidad erfolgreich zu aktivieren ist die Angabe des ResourceServlets inklusive Servlet-Mapping, sowie eines Filters erforderlich. Weiters ist ein Filter-Mapping zwischen dem TrinidadFilter und dem JSF-Servlet erforderlich. Unabhängig von weiteren Trinidad-Features ist die Grundkonfiguration aus Abbildung Grundkonfiguration von Trinidad in der web.xml immer erforderlich.
  <servlet>
    <servlet-name>resources</servlet-name>
    <servlet-class>org.apache.myfaces.trinidad.webapp.ResourceServlet</servlet-class>
  </servlet>

  <filter>
    <filter-name>trinidad</filter-name>
    <filter-class>org.apache.myfaces.trinidad.webapp.TrinidadFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>trinidad</filter-name>
    <servlet-name>Faces Servlet</servlet-name>
  </filter-mapping>

  <servlet-mapping>
    <servlet-name>resources</servlet-name>
    <url-pattern>/adf/*</url-pattern>
  </servlet-mapping>
Abbildung: Grundkonfiguration von Trinidad in der web.xml
faces-config.xml
Schlussendlich muss der Render-Kit von Trinidad als Default-Render-Kit eingetragen werden (Abbildung Ausschnitt aus der faces-config.xml für die Konfiguration von Trinidad ).
    <application>
        <default-render-kit-id>org.apache.myfaces.trinidad.core</default-render-kit-id>
    </application>
Abbildung: Ausschnitt aus der faces-config.xml für die Konfiguration von Trinidad
Sind die bisher beschriebenen Konfigurationsschritte durchgeführt, so kann Trinidad verwendet werden. In der Beispielapplikation wird wie bereits in den vorhergehenden Kapiteln Facelets statt JSP verwendet. Neben den bisherigen Setup-Schritten für Facelets ist eine zusätzliche Konfiguration für die erfolgreiche Verbindung von Trinidad und Facelets erforderlich. Diese Konfiguration wird im nachfolgenden Abschnitt beleuchtet.

11.2.2.2 Weitere web.xml Kontextparameter

Wird wie in der Beispielapplikation Facelets verwendet, so ist ein weiterer Parameter erforderlich. Durch den Parameter in Abbildung Ausschnitt aus der web.xml für die Verbindung von Trinidad und Facelets wird festgelegt, dass spezifische Implementierungen für Facelets innerhalb von Trinidad aktiv werden.
    <context-param>
        <param-name>org.apache.myfaces.trinidad.ALTERNATE_VIEW_HANDLER</param-name>
        <param-value>com.sun.facelets.FaceletViewHandler</param-value>
    </context-param>
Abbildung: Ausschnitt aus der web.xml für die Verbindung von Trinidad und Facelets
An dieser Stelle ist die erforderliche Minimalkonfiguration für die ersten Schritte in der Beispielapplikation abgeschlossen. Dieser Stand inklusive eines rudimentären Templates ist als Step tr00 als Download verfügbar.

Neben dem alternativen View-Handler gibt es für die Konfiguration von Trinidad folgende Kontextparameter:

org.apache.myfaces.trinidad.CACHE_VIEW_ROOT
org.apache.myfaces.trinidad.CLIENT_STATE_METHOD
org.apache.myfaces.trinidad.CLIENT_STATE_MAX_TOKENS
org.apache.myfaces.trinidad.USE_APPLICATION_VIEW_CACHE
org.apache.myfaces.trinidad.DEBUG_JAVASCRIPT
org.apache.myfaces.trinidad.DISABLE_CONTENT_COMPRESSION
org.apache.myfaces.trinidad.CHECK_FILE_MODIFICATION
org.apache.myfaces.trinidad.ENABLE_LIGHTWEIGHT_DIALOGS
org.apache.myfaces.trinidad.CHANGE_PERSISTENCE
org.apache.myfaces.trinidad.DIALOG_NAVIGATION_PREFIX
org.apache.myfaces.trinidad.DISABLE_DIALOG_OUTCOMES
Abbildung: Liste der web.xml Kontextparameter für Trinidad
Einige Parameter sind selbsterklärend oder betreffen Features, welche in den folgenden Kapiteln im Detail behandelt werden. Dennoch folgt bereits hier eine grundlegende Erläuterung dieser Konfigurationsmöglichkeiten.

org.apache.myfaces.trinidad.CACHE_VIEW_ROOT
Default-Wert: true

Dieser Parameter ermöglicht die De-/Aktivierung der Caching Funktionalität für View Roots. D.h., dass ein Postback schneller abgearbeitet werden kann, weil der ViewRoot nicht erneut aufgebaut werden muss. Dies ist vor allem bei Ajax intensiven Applikationen eine gute Möglichkeit, um Performance zu optimieren. Allerdings ist zu beachten, dass andere JSF Erweiterungen wie bspw. t:saveState aus der Tomahawk Bibliothek, sowie bestimmte Seiten nicht mehr funktionieren. Ist dies der Fall und es ergeben sich unerklärliche Phänomene, die ohne Trinidad nicht auftreten, dann könnte es an der Aktivierung dieses Features liegen. Somit bleibt hier nur noch die Deaktivierung dieser Funktionalität.

org.apache.myfaces.trinidad.CLIENT_STATE_METHOD
Default: token
Mögliche Werte: token, all

Unter Voraussetzung, dass client-side state saving aktiviert ist (siehe javax.faces.STATE_SAVING_METHOD ) kann mit diesem Parameter beeinflusst werden, ob nur ein Token ( " token" ) oder der serialisierte State ( " all" ) auf der Clientseite verwendet wird. Wird der Default Wert " token" verwendet, so wird der State in der Session gehalten und ein Token zur Identifikation des entsrpechenden States am Client gespeichert. Außerdem wird Failover unterstützt. Mit " all" kommt das Standard-JSF-Vorgehen zum Einsatz, bei welchem der State in einem Hidden-Field serialisiert abgelegt wird. Der Vorteil ist, dass die HttpSession nicht belastet wird. Allerdings müssen somit mehr Daten an den Client geschickt werden.

org.apache.myfaces.trinidad.CLIENT_STATE_MAX_TOKENS
Default: 15
Mögliche Werte: gültige Integer Zahlen

Wenn bei org.apache.myfaces.trinidad.CLIENT_STATE_METHOD der Default-Wert " token" zum Einsatz kommt, dann kann mit diesem Parameter festgelegt werden, wie viele Tokens zur Verfügung stehen, bevor der älteste gelöscht wird und somit der entspreche State nicht mehr gecached wird. Hierbei sind zwei Nebeneffekte zu beachten. Einerseits muss eine sinnvolle Zahl gewählt werden, wenn der Back-Button des Browsers, oder mehrere Browserfenster unterstützt werden soll. Außerdem ist eine entsprechend hohe Zahl anzugeben, wenn Seiten mit Frames zum Einsatz kommen sollen.

org.apache.myfaces.trinidad.USE_APPLICATION_VIEW_CACHE
Default: false

Wenn bei org.apache.myfaces.trinidad.CLIENT_STATE_METHOD der Default-Wert " token" zum Einsatz kommt ist hier eine Performancesteigerung möglich, da durch die Aktivierung dieser Funktionalität Seiten gecached werden. Für die Entwicklung ergibt sich somit der Nachteil, dass geänderte Seiten einen Neustart der Webapplikation erfordern. Der Vorteil dieses Features ist eine höhere Skalierbarkeit.

org.apache.myfaces.trinidad.DEBUG_JAVASCRIPT
Default: false

Wird dieses Feature aktiviert, so wird JavaScript nicht obfuscated und sämtliche Kommentare werden ebenfalls an den Client gesendet. Somit dient dieses Feature, wie bereits der Name vermuten lässt, dem komfortableren Debugging von JavaScript zur Entwicklungszeit.

org.apache.myfaces.trinidad.DISABLE_CONTENT_COMPRESSION
Default: true

Per Default werden die Namen der CSS Style-Klassen für Skinning durch Trinidad komprimiert. Somit müssen weniger Daten übertragen werden. Allerdings sind die Style-Klassen nicht mehr ohne weiteres lesbar. Um die ursprünglichen Namen zu verwenden kann dieses Feature deaktiviert werden. Dies kann primär bei der Entwicklung hilfreich sein.

org.apache.myfaces.trinidad.CHECK_FILE_MODIFICATION
Default: false

Dieses Parameter ist ebenfalls für die Entwicklung gedacht und ermöglicht, dass jede geänderte JSP neu verarbeitet wird. Außerdem werden entsprechende States (saved states) verworfen.

org.apache.myfaces.trinidad.ENABLE_LIGHTWEIGHT_DIALOGS
Default: false

Wird dieses Feature aktiviert, so werden Dialoge nicht in einem neuen Browser Fenster geöffnet, sondern es wird ein iframe über die aktuelle Seite gelegt. In diesem iframe wird die entsprechende Dialogseite angezeigt. Innerhalb des iframes ist wie gewohnt Skinning möglich.

org.apache.myfaces.trinidad.CHANGE_PERSISTENCE
Default: -
Mögliche Werte: session oder Klassenname

Wird kein Werte gesetzt, so wird org.apache.myfaces.trinidadinternal.change.NullChangeManager verwendet. Wird Session angegeben, so wird org.apache.myfaces.trinidadinternal.change.SessionChangeManager verwendet. Hierbei wird als Persistenzstrategie die HttpSession verwendet. Durch die Angabe eines voll qualifizierten Klassennames kann hier eine eigene Implementierung des ChangeManagers angegeben werden.

org.apache.myfaces.trinidad.DIALOG_NAVIGATION_PREFIX
Default: dialog:
Im Kapitel über Trinidad Dialoge wird das Dialog-Feature im Detail erklärt. Mit diesem Parameter kann der Prefix zum Start von Dialogen geändert werden.

org.apache.myfaces.trinidad.DISABLE_DIALOG_OUTCOMES
Default: false

Mit diesem Parameter können Dialog zentral deaktiviert werden, selbst wenn in einer/mehreren Seite(n) ein gültiger Dialog-Prefix zum Einsatz kommt.
Dieses Parameter dient lediglich für Tests von Rendererkits und bewirkt, dass keine Bilder erzeugt/gerendert werden.

11.2.2.3 trinidad-config.xml

Zusätzlich zu der Konfiguration in der web.xml sind weitere Konfigurationen möglich, welche in der Datei trinidad-config.xml angegeben werden können. Damit Trinidad auf diese Konfigurationsdatei zugreifen kann, muss diese im WEB-INF Verzeichnis der Webapplikation angelegt werden. Ein wichtiger Vorteil dieser XML basierten Konfiguration ist die optionale Verwendung von EL-Ausdrücken direkt in den Konfigurationseinträgen. Jeder Eintrag kann fix oder per EL-Ausdruck definiert werden. Erfolgt eine Angabe per EL-Ausdruck, so wird diese bei jedem Request ausgewertet.
Abbildung Beispiel für einen Ausschnitt aus trinidad-config.xml zeigt beide Möglichkeiten. Der Debugging Modus ist hier statisch angegeben. Im Gegensatz hierzu wird Skinning per EL-Ausdruck dynamisch entschieden.
<?xml version="1.0"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
  <debug-output>true</debug-output>

  <skin-family>
    #{view.locale.language=='en' ? 'SkinA' : 'SkinB'}
  </skin-family>
</trinidad-config>
Abbildung: Beispiel für einen Ausschnitt aus trinidad-config.xml
Übersicht Konfigurationsmöglichkeiten
Einige Parameter sind selbsterklärend oder betreffen Features, welche in den folgenden Kapiteln im Detail behandelt werden. Dennoch folgt bereits hier eine grundlegende Erläuterung dieser Konfigurationsmöglichkeiten.

Hinweis:
Sämtliche Parameter dieser Konfigurationsdatei sind über die Klasse org.apache.myfaces.trinidad.context.RequestContext verfügbar.

debug-output
Dieser Konfigurationseintrag ist für die Entwicklungszeit gedacht und bewirkt, dass der Response formatiert wird, sodass dieser besser lesbar ist. Außerdem werden Kommentare bereitgestellt, die anzeigen, welche Komponente welchen HTML Block gerendert hat. Außerdem werden diverse evt. vorhandene Markup Probleme angezeigt.

client-validation
Mögliche Werte: INLINE, ALERT, DISABLED

Dieser Konfigurationseintrag legt das Verhalten von clientseitiger Konvertierung und Validierung fest. Durch INLINE werden Fehler in der Seite angezeigt, im Gegensatz hierzu wird mit ALERT ein JavaScript Alert Dialog angezeigt. Mit DISABLED wird die Anzeige eines Fehlers unterdrückt und Konvertierung und Validierung werden nur serverseitig durchgeführt.

client-validation-disabled
Dieser Konfigurationseintrag ist veraltet und wurde vor der Einführung vom Konfigurationseintrag " client-validation" verwendet.

output-mode
Mögliche Werte: default, printable und email

Mit diesem Konfigurationseintrag kann der Output für verschiedene Anwendungsfälle optimiert werden.

skin-family
Hier kann die Skin-Familie angegeben werden, welche für das Skinning verwendet werden soll.

accessibility-mode
Mögliche Werte: default, inaccessible, screenReader

Dieser Konfigurationseintrag steuert den Output in Hinblick auf Accessibility. Mit " default" werden Accessibility-Features unterstützt. Hingegen optimiert " inaccessible" den Output, indem keine Accessibility Angaben/Optimierungen in den Output aufgenommen werden. Mit " screenReader" werden spezielle Optimierungen für ScreenReader in den Output aufgenommen.

oracle-help-servlet-url
Über diesen Konfigurationseintrag kann eine URL angegeben werden, welche die Oracle Help for the Web (OHW) Funktionalität bereitstellt.

page-flow-scope-lifetime
Default: 15

Im Gegensatz zu anderen Konfigurationseinträgen ist dieser nicht EL-fähig. Mit diesem Eintrag kann angegeben werden, wie viele pageFlowScope Instanzen maximal gleichzeitig gehalten werden.

time-zone
Default: Wert des Browsers

Dieser Eintrag dient zum Setzen der Zeitzone, welche von org.apache.myfaces.trinidad.converter.DateTimeConverter verwendet werden soll.

two-digit-year-start
Default: 1950

Dieser Eintrag definiert den Jahresoffset, welche von org.apache.myfaces.trinidad.converter.DateTimeConverter verwendet werden soll, um Jahresangaben mit 2 Stellen zu parsen.

right-to-left
Default: false

Mit diesem Konfigurationseintrag kann die Leserichtung geändert werden. D.h. beim Rendering wird der Text entsprechend zur Leserichtung ausgegeben. Die Lesereichung rechts-nach-links ist für verschiedene Sprachen erforderlich. Sollen mehrere Sprachen unterstützt werden, so ist hier die Verwendung eines entsprechende EL-Ausdrucks sinnvoll.

number-grouping-separator
Default: Wird durch das aktuelle Lokale definiert

Dieser Eintrag gibt das Tausender-Trennzeichen bei Zahlen an, welches von org.apache.myfaces.trinidad.converter.NumberConverter verwendet werden soll.

decimal-separator
Default: Wird durch das aktuelle Lokale definiert

Dieser Eintrag gibt das Zeichen für den Dezimalpunk bei Zahlen an, welches von org.apache.myfaces.trinidad.converter.NumberConverter verwendet werden soll.

currency-code
Dieser Eintrag gibt den Währungscode nach ISO 4217 an, welcher zur Formatierung von Währungsangaben von org.apache.myfaces.trinidad.converter.NumberConverter verwendet wird.

uploaded-file-processor
Über diesen Konfigurationseintrag kann ein alternativer UploadProcessor angegeben werden. Dieser muss das Interface org.apache.myfaces.trinidad.webapp.UploadedFileProcessor implementieren. Die Standardimplementierung speichert einen Upload temporär, bevor eine weitere Verarbeitung erfolgen kann. Dies kann bspw. durch eine eigene Implementierung übersprungen werden.

formatting-locale
Hier kann ein Locale angegeben werden, welches für Converter verwendet wird, welche kein explizites Locale haben. Das Format entspricht dem Standardformat, welches bspw. aus der faces-config.xml bereits bekannt ist (also bspw. de_AT).

11.3 myGourmet meets Trinidad

In diesem Abschnitt wird die bisher entwickelte Beispielapplikation myGourmet auf Trinidad umgestellt. Die Basiskonfiguration inklusive eines rudimentären Templates ist im Step tr00 als Download verfügbar. Aufbauend auf dieser Basiskonfiguration wird in den nachfolgenden Abschnitten die Beispielapplikation schrittweise mit Trinidad und Facelets implementiert.

11.3.1 Ausgangslage

Die Grundstruktur des Projekts, sowie grundlegende Konzepte (unabhängig von Trinidad) wurden bereits in den vorherigen Kapiteln ausführlich erläutert. In Step tr01 wird die erste Seite mit Funktionalität gefüllt. Als Startpunkt dient neben einer fertig konfigurierten Applikation die Trinidad Document-Komponente.

tr:document
Der erste Unterschied zu Applikationen mit Standardkomponenten besteht in der Verwendung des tr:document -Tags. Dieser Tag muss die gesamte Seite umschließen und ist erforderlich, um Trinidad-Features zu nutzen. Aus diesem Grund wird dieser Tag in der Beispielapplikation zentral im Template verwendet.

Auch tr:document ist nicht an HTML gebunden. Daher sind HTML-spezifische Komponenten über den Namespace http://myfaces.apache.org/trinidad/html verfügbar. Aus diesem Namespace kann bspw. trh:head verwendet werden, um Informationen für den HTML-Head bereit zu stellen. Als Alternative zu trh:head gibt es ein Facet für tr:document , in welchem im Falle von HTML Inhalte für den HTML-Head angegeben werden können. Abbildung Beispiel für tr:document zeigt ein Beispiel, in welchem der Seitentitel angegeben wird.
<tr:document xmlns:tr="http://myfaces.apache.org/trinidad">
  <f:facet name="metaContainer">
    <title>myGourmet - JSF sample application</title>
  </f:facet>
</tr:document>
Abbildung: Beispiel für tr:document
In Abbildung Beispiel für tr:document ist ersichtlich, dass wie üblich für den Präfix tr ein entsprechender Namensraum angegeben werden muss, damit Trinidad Komponenten verwendet werden können. Neben diesem Namensraum gibt es in Trinidad (wie bereits erwähnt) einen weiteren, über welchen spezielle Trinidad-HTML-Komponenten zur Verfügung stehen. Für diesen Namespace wird überlicherweise trh als Prefix gewählt. Einige Tags werden im Kapitel " Advanced Trinidad Components" näher betrachtet.

11.3.2 Start von MyGourmet mit Trinidad

In der bisherigen Implementierung der Beispielapplikation wurde die Registrierung eines Kunden vorgestellt und im Detail erklärt. Aufbauend auf diesen Erläuterungen wird die Beispielapplikation auf Trinidad umgestellt. Abgesehen von der Umstellung auf tr:document werden in diesem aufbauenden Beispiel einige Standardkomponenten ersetzt. So wird bspw. der Button für die Benutzerregistrierung in Abbildung Umstellung h:commandButton auf tr:commandButton umgestellt.
<h:commandButton value="#{messages.register}" action="#{nav.register}"/>
wird zu
<tr:commandButton text="#{messages.register}" action="#{nav.register}"/>
Abbildung: Umstellung h:commandButton auf tr:commandButton
Wie in Abbildung Umstellung h:commandButton auf tr:commandButton ersichtlich führt Trinidad zusätzliche Attribute für Tags ein. Eine umfassende Auflistung je Komponente gibt es unter http://myfaces.apache.org/trinidad/trinidad-api/tagdoc.html Grundsätzlich gibt es für beinahe alle Standardkomponenten eine Entsprechung in Trinidad. Ist dies nicht der Fall, so können Trinidad Komponenten und Standardkomponenten problemlos gemischt werden. Nun stellt sich die Frage, warum Trinidad auch Standardkomponenten implementiert. Der Grund hierfür ist, dass diese Komponenten um einige Features erweitert wurden. So unterstützen auch diese Komponenten Ajax, Skinning und sämtliche anderen Vorteile, die durch Trinidad Komponenten verfügbar werden. Daher verzichtet man teilweise auf diese Features, wenn auf Standardkomponenten zurückgegriffen wird (abgesehen von möglichen workarounds, auf welche an entsprechenden Stellen hingewiesen wird).


Durch das Hinzugefügt des Buttons wird die bereits bekannte Funktionalität der Seitennavigation implementiert. Hierfür werden die verfügbaren Standardkonzepte von JSF verwendet. Die folgende Seite fällt allerdings noch recht mager aus. Dieser Zwischenstand ist als Step tr01 downloadbar. Mehr Funktionalität wird im nächsten Abschnitt hinzugefügt. Außerdem wird das PPR Feature von Trinidad im Detail erklärt.

11.4 Ajax/Partial Page Rendering (PPR) in Action

Hinter PPR, also Partial Page Rendering, verbirgt sich das Konzept, welches auch hinter Ajax steht. Zusammenfassend geht es darum, dass eine Interaktion des Users / der Userin mit der Webapplikation nicht zwangsläufig zum kompletten Refresh der Seite führt. In aktuellen Trinidad Versionen ist PPR auf Basis von XMLHttpRequests implementiert. Dies ermöglicht, dass nur Teilbereiche der Seite aktualisiert werden. Serverseitig durchläuft der Request alle Phasen, welche JSF zur Verfügung stellt. Dementsprechend können Requests auch abgekürzt werden (bspw. durch den Aufruf von FacesContext.getCurrentInstance().renderResponse(); ).

In diesem Abschnitt wird auf Step tr01 aufgebaut. Bisher unterscheidet sich das Ergebnis, welches mit Hilfe von Trinidad Komponenten erzielt wurde, nach außen noch nicht von der bisherigen Funktionalität. Dies wird sich in diesem Abschnitt ändern.

tr:form
Um PPR in der Anwendung einfacher zu gestalten unterscheidet sich tr:form von h:form , indem tr:form kein eigener Namingcontainer ist. Außerdem stellt tr:form zusätzliche Funktionalität für eine Standardschaltfläche (Attribut defaultCommand ) zur Verfügung. Weiters ermöglicht tr:form Dateiuploads. tr:form stellt auch selbst ein Attribut für PPR zur Verfügung.


<tr:document
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:tr="http://myfaces.apache.org/trinidad"
        xmlns:c="http://java.sun.com/jstl/core">
    <f:facet name="metaContainer">
        <title>myGourmet - JSF sample application</title>
    </f:facet>

    ...
        <tr:form id="form" defaultCommand="defaultButton">
	    ...
        </tr:form>
    ...

</tr:document>
Abbildung: Ausschnitt des Templates - tr:form mit defaultCommand
Hinweis:
Trinidad stellt bei Formularen wie bereits erwähnt eine Default-Button Funktionalität zur Verfügung, welche es ermöglicht, dass durch den Druck der Enter-Taste ein entsprechend registrierter Button ausgelöst wird. Hierbei muss die ID des Buttons mit dem Wert des defaultCommand-Attributes übereinstimmen. Diese Funktionalität kann natürlich auch unter Verwendung des PPR-Features weiter genutzt werden.

11.4.1 PPR mit Attributen

PPR kann durch Attribute der Trinidad Komponenten oder im Source Code angestoßen werden (siehe RequestContext ). Um das Formular für den ersten Registrierungsschritt (Seite crd_add_customer.xhtml) PPR-fähig zu machen, wird PPR mit Hilfe der entsprechenden Attribute realisiert.

Die Eingabemöglichkeiten für Kreditkartendetails sollen mit Hilfe des rendered -Attributes optional eingeblendet werden. Hierfür wird zuerst h:selectBooleanCheckbox gegen tr:selectBooleanCheckbox ausgetauscht. Zusätzlich zum valueChangeListener - und dem immediate -Attribut wird ein Attribut names autoSubmit benötigt. Dies wird auf true gesetzt, damit der PPR-Request direkt nach dem Klick auf die Checkbox abgesetzt wird. Jetzt folgt das Ein-Ausblenden der Eingabemöglichkeiten für Kreditkartendetails.


Hinweis:
Bei Command Komponenten heißt dieses Attribut partialSubmit . In beiden Fällen wird die PPR Funktionalität von Trinidad angestoßen. Als Gegenstück zu diesen Attributen gibt es ein Attribut names partialTriggers . Hier können die ID's angegeben werden, welche zu Komponenten gehören, die einen PPR-Request anstoßen können. Falls hier ID's von mehreren Command-Komponenten angegeben werden sollen, können diese mit Leerzeichen getrennt hintereinander geschrieben werden.

Komponenten, die mit dem rendered -Attribut ausgeblendet sind, werden nicht in der Seite versteckt, sondern tatsächlich nicht gerendert. Daher können diese Komponenten nicht per partialTriggers -Attribut aktualisiert werden. Daher muss auch hier eine beliebige parent-Komponente auf den entsprechenden Trigger " registriert" werden. Im vorherigen Beispiel wird daher die ID " creditCardSelectionBox" in der parent-Komponente registriert (Attribute partialTriggers ). Damit nicht das gesamte Formular aktualisiert wird kann bspw. ein separates Panel um diesen Bereich gelegt werden. Dieses Panel wird immer gerendert, jedoch nur bei Bedarf mit den entsprechenden Kindkomponenten.


Wird ein PPR-Request angestoßen und gibt es in der Seite Komponenten, die auf die entsprechenden ID registriert sind, dann werden diese Komponenten inkl. ihrer Kindkomponenten aktualisiert. Im zuvor aufgeführten Beispiel wird für PPR das autoSubmit -Attribut verwendet. Dies bewirkt im vorliegenden Fall, dass der ValueChangeListener (serverseitig) ausgeführt wird, sobald eine entsprechende Interaktion des Users/der Userin mit der Komponente erfolgt. Im Vergleich hierzu wurde der ValueChangeListener ohne PPR bisher erst ausgeführt, nachdem das gesamte Formular abgeschickt wurde. (Achtung: es wird nicht bei jeder kleinen Änderung ein PPR-Request erzeugt. PPR wird normalerweise durch einen Klick oder durch das Drücken der Tab-Taste angestoßen, da somit die User-Interaktion für diese Inputkomponente für beendet gilt.)

Hinweis:
Muss auf Standardkomponenten zurückgegriffen werden, welchen die speziellen PPR Attribute nicht zur Verfügung stellen, dann können diese Komponenten in Trinidad Komponenten eingebettet werden. Diese umschließende Trinidad Komponente kann bei Bedarf angestoßen werden. Somit wird erreicht, dass auch die eingebetteten Standardkomponenten durch die PPR Funktionalität von Trinidad aktualisiert werden.


<tr:panelFormLayout partialTriggers="creditCardSelectionBox">
    <tr:selectBooleanCheckbox
	id="creditCardSelectionBox"
	label="#{messages.use_credit_card}"
	autoSubmit="true" immediate="true"
	value="#{registrationPageBean.customer.useCreditCardPerDefault}"
	valueChangeListener="#{registrationPageBean.useCreditCard}"/>
    <tr:group rendered="#{registrationPageBean.customer.useCreditCardPerDefault}">
        <tr:selectOneListbox
	label="#{messages.credit_card_type}"
	value="#{registrationPageBean.creditCardType}"
	required="true">
            <f:selectItems value="#{registrationPageBean.creditCardTypes}"/>
        </tr:selectOneListbox>
        <tr:inputText
	label="#{messages.credit_card_number}"
	value="#{registrationPageBean.customer.creditCardNumber}"
	required="true"/>
    </tr:group>
</tr:panelFormLayout>
public void useCreditCard(ValueChangeEvent event)
{
    this.customer.setUseCreditCardPerDefault( (Boolean)event.getNewValue() );
    FacesContext.getCurrentInstance().renderResponse();
}
Abbildung: Umstellung der optionalen Kreditkartendetails
Hinweis:
Die Alternativ zur Verwendung des rendered -Attributes und der Aktualisierung der parent-Komponente ist die Verwendung der CSS Lösung ( inlineStyle=" display:none" )

Hinweis:
Je kleiner der aktualisierte Bereich in der Seite ist, desto unauffälliger verhält sich die Seite bei der Aktualisierung der Informationen.

Alle weiteren Umstellungen dieser ersten Seite auf Trinidad Komponenten haben keinen Bezug zum PPR-Feature von Trinidad. Die entsprechenden Eigenschaften der Komponenten werden im Kapitel " Advanced Trinidad Components" besprochen.

Hinweis:
In der Beispielapplikation wird tr:selectManyListbox verwendet. Allerdings gibt es in der aktuellen Trinidad Version (1.x.4) einen Bug, sodass der Inhalt nicht korrekt dargestellt wird (im Vergleich zu der Standardkomponente). Ein entsprechender Patch ist bereits verfügbar ( http://issues.apache.org/jira/browse/TRINIDAD-812 ). Alternativ kann die Standardkomponente verwendet werden, welche im Beispiel durch <ui:remove> " ausgeblendet " wird.

Hinweis:
Im bisher beschriebenen Beispiel kommt es zu einem Fehlverhalten, welches aufgrund eines Bugs verursacht wird. Ein entsprechender Patch wurde bereits erstellt und wird in Version 1.x.5 verfügbar sein ( http://issues.apache.org/jira/browse/TRINIDAD-842 ).

11.4.2 PPR per RequestContext im Source Code

In einem weiteren Schritt wird die Seite crd_add_customer_address.xhtml umgestellt. Hier wird PPR sowohl durch die entsprechenden Attribute, als auch durch den RequestContext verwendet. Zuerst wird die Funktionalität der Seite erweitert, indem angelegte Adressen direkt auf der Seite in einer Tabelle als Überblick dargestellt werden. Diese Tabelle soll sich inhaltlich anpassen, sobald Informationen hinzugefügt oder gelöscht werden. Allerdings soll nicht die gesamte Seite neu dargestellt werden, sondern mit Hilfe von PPR nur das Formular und die Tabelle selbst. Außerdem soll das Formular gelöscht werden, sobald eine Adresse hinzugefügt wurde.
Daher stellen wir drei Schaltflächen zur Verfügung. Die erste Schaltfläche übernimmt die eingegebene Adresse und ermöglicht, dass eine weitere Adresse eingegeben werden kann. Ein zweiter Button ermöglicht es dem/der UserIn, dass die Adresse übernommen wird und automatisch die nächste Seite aufgerufen wird. Des weiteren soll ein dritter Button eingeblendet werden, sobald mindestens ein Eintrag in der Tabelle enthalten ist. Dieser Button soll ermöglichen, dass zum nächsten Schritt der Benutzerregistrierung gewechselt werden kann, ohne das Formular mit einer weiteren Adresse auszufüllen. Somit ergibt sich der Seiten-Ausschnitt aus Abbildung Schaltfälchen für die Verarbeitung von Adresseingaben .
<tr:commandButton id="add" text="#{messages.add}" partialSubmit="true" actionListener="#{registrationPageBean.addAddress}"/>
<tr:commandButton id="defaultButton" text="#{messages.add_and_next}" actionListener="#{registrationPageBean.addAddress}" action="#{nav.showCustomerSummary}"/>
<tr:commandButton text="#{messages.next}" immediate="true" action="#{nav.showCustomerSummary}" rendered="#{registrationPageBean.showAddressTable}"/>
Abbildung: Schaltfälchen für die Verarbeitung von Adresseingaben
Der zweite Button in Abbildung Schaltfälchen für die Verarbeitung von Adresseingaben wird durch die ID als Default Button festgelegt. Das entsprechende Gegenstück wurde bereits vorgestellt (Attribut defaultCommand - siehe Abbildung Ausschnitt des Templates - tr:form mit defaultCommand ). Somit wird dieser Button aufgerufen, sobald in der Form die Enter-Taste gedrückt wird.
Alle betroffenen Komponenten sind durch eine Trinidad Komponente (bspw. durch den Tag tr:panelGroupLayout ) gruppiert. Daher ist es durchaus sinnvoll das gesamte Panel zu aktualisieren, sobald der User/die Userin mit diesem Formular interagiert. Daher wird in diesem Tag das Attribut partialTriggers angegeben. Die ID, die in diesem Attribut hinterlegt wird, entspricht der ID eines Buttons. Dieser Schritt ist in Abbildung partialTriggers zur Aktualisierung des Formulars ausschnittsweise dargestellt.
<tr:panelGroupLayout partialTriggers="add">
   ...
   <tr:panelFormLayout>...</tr:panelFormLayout>
   <tr:commandButton id="add" text="#{messages.add}"
      partialSubmit="true" actionListener="..."/>
   ...
</tr:panelGroupLayout>
Abbildung: partialTriggers zur Aktualisierung des Formulars
Alle Informationen, welche nicht unmittelbar für dieser Erklärung wichtig sind, wurden hier (im Vergleich zu der Seite in der Beispielapplikation) weggelassen. Sobald der Button durch den User angeklickt wird, wird ein PPR-Request angestoßen, welcher auf der Serverseite wie ein normaler Request den ActionListener ausführt. Außerdem wird in diesem konkreten Beispiel die Komponente mit dem Tag tr:panelGroupLayout aktualisiert (inkl. aller Kindkomponenten).

Das Eingabeformular selbst ist auch mit Trinidad Komponenten realisiert. Grundsätzlich sind diese sehr ähnlich zu den Standardkomponente. Eine genaue Betrachtung folgt im Kapitel " Advanced Trinidad Components" . Der ActionListener aus Abbildung ActionListener zum Hinzufügen der eingegebenen Adresse ist dafür verantwortlich, dass die eingegebene Adresse gespeichert wird und die Adresse für das Eingabeformular zurückgesetzt wird. Somit wird die eingegebene Adresse der Tabelle hinzugefügt wird und das Formular zurückgesetzt.
    public void addAddress(ActionEvent event)
    {
        this.customer.getAddresses().add( this.address );
        this.address = new Address();
    }
Abbildung: ActionListener zum Hinzufügen der eingegebenen Adresse
Aus Abbildung ActionListener zum Hinzufügen der eingegebenen Adresse ist ersichtlich, dass hierfür keine Trinidad spezifischen Teile erforderlich sind. Die Adresse wird dem Kunden hinzugefügt und der Address-Referenz der PageBean wird ein neues Address Objekt zugewiesen. Durch das Attribut partialTriggers wird in diesem Fall das entsprechende Panel aktualisiert, wodurch die neuen Informationen auf der Seite dargestellt werden. In Abbildung partialTriggers zur Aktualisierung des Formulars wurde die Tabelle selbst nicht dargestellt. Diese wird ebenfalls durch Trinidad Komponenten aufgebaut. Hier gibt es allerdings keine Besonderheit in Hinsicht auf PPR. Somit folgt die Erläuterung der Komponente selbst im Kapitel " Advanced Trinidad Components" .

Für das Löschen eines Tabelleneintrages und die Aktualisierung der Tabelle wird in der Beispielapplikation der RequestContext verwendet, um PPR per Source Code zu erläutern. Gibt es bspw. bereits mehrere Adresseinträge in der Tabelle und soll einer dieser Einträge wieder gelöscht werden, so kann dies bspw. über einen Link realisiert werden. Dieser Link wird mit einem ActionListener verbunden, welcher den Eintrag löscht und die Aktualisierung der Tabelle anstößt. Hierfür wird der Tabelle eine ID vergeben (in der Beispielapplikation " addressTable" ), welche im Source Code verwendet werden kann, um die entsprechende Komponente zu suchen. Diese Komponente wird dann den " partial targes" zugewiesen. Somit wird nur die Tabelle aktualisiert. D.h. wenn bereits begonnen wurde eine neue Adresse einzugeben und zwischendurch wird ein Tabelleneintrag gelöscht, wird nur die Tabelle aktualisiert und das (teilweise) ausgefüllte Formular bleibt erhalten. Somit kann selbst nach dieser Unterbrechung mit der Eingabe der Adresse fortgefahren werden. Abbildung Verwendung von RequestContext zur Verwendung von PPR im Source Code zeigt alle hierfür relevanten Ausschnitte der Tabelle.
            <tr:table id="addressTable"
                      value="#{registrationPageBean.customer.addresses}"
                      var="entry"
                      ...
                      immediate="true"
                      autoSubmit="true">
                      ...
                <tr:column>
                    <tr:commandLink text="#{messages.delete}" partialSubmit="true" actionListener="#{registrationPageBean.deleteAddress}" immediate="true"/>
                </tr:column>
            </tr:table>
    public void deleteAddress(ActionEvent event)
    {
        CoreTable table = findTable( event.getComponent() );

        customer.getAddresses().remove( table.getRowData() );

        if(RequestContext.getCurrentInstance().isPartialRequest( FacesContext.getCurrentInstance() ))
        {
            triggerPartialPageRenderingForComponent( "addressTable" );
        }
    }

    private void triggerPartialPageRenderingForComponent(String componentId)
    {
        FacesContext context = FacesContext.getCurrentInstance();
        RequestContext.getCurrentInstance().addPartialTarget( context.getViewRoot().findComponent( componentId ) );
    }
Abbildung: Verwendung von RequestContext zur Verwendung von PPR im Source Code

11.4.3 PPR über Attribute vs. PPR mit RequestContext (per Source Code)

Grundsätzlich ist die Verwendung der entsprechenden Attribute empfehlenswert. Ist die Aktualisierung von Seitenbereichen allerdings von Bedingungen im Source Code abhängig oder stößt die Lösung mit Hilfe der PPR-Attribute an Grenzen, so kann PPR per RequestContext im Source Code verwendet werden. Der Nachteil von PPR per Source Code ist, dass eine (wenn auch kurze) Implementierung erforderlich ist und PPR-Mechanismen somit nicht direkt in der Seite ersichtlich sind. Im Gegensatz hierzu wird PPR über Attribute automatisch von Trinidad analysiert und durchgeführt. Ein gutes Beispiel für " technische" Grenzen ist Issue http://issues.apache.org/jira/browse/TRINIDAD-842 . Als workaround kann hier bspw. PPR per RequestContext verwendet werden, um die Aktualisierung der gesamten Form anzustoßen. Somit bestehen bis zur Lösung bspw. eines Bugs geringfügige Nachteile (die Implementierung des workarounds und der Nachteil, dass ein größerer Bereich aktualisiert werden muss), allerdings ist es möglich die Funktionalität zur Verfügung zu stellen.

11.4.4 PPR und JSF Namingcontainer

Soll PPR zwischen verschiedenen Namingcontainer verwendet werden, so muss der Namingcontainer auch im partialTriggers -Attribut angegeben werden. Dies sieht bspw. wie folgt aus:

partialTriggers=" theSubform:theLink"

Für Componenten im root der Seite wird nur ein Doppelpunkt angegeben:
partialTriggers=" :someRootComponent"

Gibt es mehrere Namingcontainer und soll auf den darüber liegenden verwiesen werden, dann müssen 2 Doppelpunke angegeben werden. Für jeden weiteren darüber liegenden Namingcontainer muss ein weiterer Doppelpunk hinzugefügt werden.

11.4.5 PPR Funktionalität ohne zusätzlicher Konfiguration

Die folgenden Trinidad-Elemente unterstützen PPR ohne zusätzliche Konfigurationen für Quelle und Ziel:
Einige dieser Bestandteile werden im Detail in den folgenden Kapiteln vorgestellt.

11.4.6 Weitere PPR Hints

Natürlich ist es möglich Command-Komponenten mit dem Attribut partialSubmit zu versehen, ohne die ID dieser Komponente als partialTrigger zu registrieren. Dies bewirkt den Aufruf der entsprechenden Serverlogik, allerdings bleibt die Seite unverändert (sofern serverseitig nicht PPR per RequestContext zum Einsatz kommt). Daher muss man diese Möglichkeit mit bedacht einsetzten, da der User/die Userin in diesem Fall keine Rückmeldung von der Seite bekommt, ob die entsprechende Aktion tatsächlich durchgeführt wurde.

Eine weitere Möglichkeit, welche bisher nicht erläutert wurde ist, dass Command-Komponenten ebenfalls das partialTriggers -Attribut besitzen. Somit können diese von anderen Komponenten (oder sich selbst) angestoßen werden, damit eine Aktualisierung der Command-Komponente selbst erfolgt. Beispielsweise wäre denkbar über diesen Mechanismus Command-Komponenten zu de-/aktivieren.

Bisher wurden hauptsächlich ActionEvents betrachtet. Allerdings stößt jedes serverseitiges Event ( ValueChangeEvent , SortEvent , ...) eine Aktualisierung der Entsprechenden Komponenten an, sofern die entsprechende Trigger registriert wurden.

Das Aktualisieren einzelner Komponenten über den RequestContext ist nur in einem PPR-Request sinnvoll, da außerhalb eines solchen Requests sowieso die gesamte Seite aktualisiert wird. Es ist jedoch nicht erforderlich im Source Code Unterscheidungen zu treffen. Wird die Funktion addPartialTarget außerhalb eines PPR-Requests ausgeführt, so hat dies keine weiteren Konsequenzen. Muss aus anderen Gründen festgestellt werden, ob es sich um einen PPR-Request handelt oder nicht, dann kann dies über den Funktionsaufruf isPartialRequest aus Abbildung Verwendung von RequestContext zur Verwendung von PPR im Source Code herausgefunden werden. Im vorliegenden Fall wird der deleteAddress-ActionListener vor der Bearbeitung einer Adresse wiederverwendet. Wie bereits erwähnt ist die Unterscheidung mit Hilfe dieses Methodenaufrufs nicht erforderlich. Im Falle einer Adressbearbeitung wird die Adresse durch einen speziellen ActionListener in den Request Scope kopiert und durch den deleteAddress-ActionListener aus der Adressen-Liste entfernt. Abschließend wird eine neue Seite angezeigt. Somit stößt der Link zum Löschen eines Eintrags den gleichen ActionListener an, wie der Link für die Bearbeitung eines Eintrags. Aufgrund des Seitenwechsels bei der Bearbeitung eines Eintrags bedarf es grundsätzlich keiner Unterscheidung zwischen den beiden Request-Arten. Würde der PPR-Request allerdings eine fachlich aufwändigere Verarbeitung bewirken, so könnte mit dieser Unterscheidungsmöglichkeit diese Verarbeitung nur auf den PPR-Request beschränkt werden.

11.5 Trinidad Dialog Framework in Action

In Applikationen ist eine häufige Anforderung gewisse Schritte in einem Wizard zur Verfügung zu stellen. Beispielsweise soll die ursprüngliche Seite geladen bleiben und für die Eingabe zusätzlicher Informationen ein neues Fenster angezeigt werden. Nachdem diese Eingaben abgeschlossen sind, wird der Wizard (auch Dialog genannt) geschlossen und die ursprüngliche Seite ist wieder im Vordergrund. Außerdem soll diese Seite mit den Ergebnissen des Dialogs weiter arbeiten können.
In der bisherigen Implementierung von myGourmet gibt es bereits ein Dialogszenario. Die Benutzerregistrierung erfolgt in aufeinander folgenden Schritten und kann somit als Dialog bezeichnet werden.
Bei Trinidad gibt es folgende Möglichkeiten Dialoge zur Verfügung zu stellen:
Im nachfolgenden Abschnitt werden alle drei Möglichkeit im Detail betrachtet.

11.5.1 Dialoge im gleichen Fenster

Soll ein Dialog für den User als solches nicht von anderen Seiten zu unterscheiden sein, oder unterstützt ein Device keine neuen Fenster, so kann/wird der Dialog im gleichen Fenster dargestellt. Abbildung Beispiel für eine Navigationsregel und eine Action-Methode, um einen Dialog zu starten zeigt, dass einerseits der String der Action-Methode geändert werden muss und andererseits ist die entsprechende Anpassung der Navigation erforderlich.
    public String register()
    {
        return "dialog:register";
    }
    <navigation-rule>
        <from-view-id>/pages/home.xhtml</from-view-id>
        <navigation-case>
            <from-outcome>dialog:register</from-outcome>
            <to-view-id>/pages/add_customer.xhtml</to-view-id>
        </navigation-case>
    </navigation-rule>
Abbildung: Beispiel für eine Navigationsregel und eine Action-Methode, um einen Dialog zu starten
Durch diese beiden Änderungen wird ein neuer Dialog begonnen. Getriggert wird dies durch den Prefix " dialog:" . Bei Bedarf kann dieser Prefix durch das Kontextparameter org.apache.myfaces.trinidad.DIALOG_NAVIGATION_PREFIX in der web.xml geändert werden. Allerdings wird dieser Dialog im gleichen Fenster angezeigt und ist somit für den/die UserIn nicht als solcher erkennbar. Abgesehen von der Unterstützung alternativer Devices (bspw. PDAs) kann man auch in einem solchen Dialog sämtliche Dialog-Features nutzen, welche Trinidad zur Verfügung stellt und im Laufe des Kapitels vorgestellt werden.

Hinweis:
Da der Dialog jetzt nicht mehr das Template der gesamten Seite benötigt wird für Dialoge ein eigenes Template erstellt und eingebunden.

11.5.2 Dialoge als Popup

Zum Starten eines Popup-Dialoges sind ebenfalls die Schritte aus Abbildung Beispiel für eine Navigationsregel und eine Action-Methode, um einen Dialog zu starten erfolgreich. Zusätzlich muss das useWindow -Attribut der Command-Komponente auf true gesetzt werden. In der Beispielapplikation ist dies die Komponente tr:commandButton in der Seite home.xhtml . Damit die Seite, von welcher der Dialog gestartet wird, nicht neu geladen werden muss, ist es sinnvoll das Attribut partialSubmit ebenfalls auf true zu setzen. Abbildung Button, um einen Dialog zu starten zeigt wie sich diese Änderung in der Beispielapplikation präsentiert.
<tr:commandButton id="defaultButton" text="#{messages.register}" action="#{nav.register}" useWindow="true" partialSubmit="true"/>
Abbildung: Button, um einen Dialog zu starten
Alternativ können durch die Attribute windowWidth und windowHeight die Dimensionen des Fensters festgelegt werden. Abbildung Button, um einen Dialog in einer bestimmten Fenstergröße zu starten zeigt beispielhaft die Verwendung dieser Attribute.

<tr:commandButton id="defaultButton" text="#{messages.register}" action="#{nav.register}" immediate="true" useWindow="true" partialSubmit="true" windowWidth="800" windowHeight="600"/>
Abbildung: Button, um einen Dialog in einer bestimmten Fenstergröße zu starten

11.5.3 Leichtgewichtige Dialoge

Leichtgewichte Dialog unterscheiden sich vom Vorgehen (bezüglich der Verwendung) beinahe nicht von Popup-Dialogen. Der einzige Unterschied ist das Kontextparameter org.apache.myfaces.trinidad.ENABLE_LIGHTWEIGHT_DIALOGS in der web.xml . Wird dieses auf true gesetzt, so werden keine Popups erzeugt, sondern es wird ein div über die Seite gelegt, welches ein iframe enthält. Dieses iframe ist für die Darstellung der Dialogseiten verantwortlich. Die Seite im Hintergrund ist außerdem disabled und kann nicht angesprochen werden, bis der Dialog wieder geschlossen wird.

Hinweis:
Im Vergleich zu Popup-Dialogen funktionieren die Attribute windowWidth und windowHeight bis inkl. Version 1.x.4 nur, wenn beide Attribute angegeben sind. Dieses Problem ist ab Version 1.x.5 behoben (siehe https://issues.apache.org/jira/browse/TRINIDAD-841).

11.5.4 Dialog - das Finish

Ein Dialog stellt eine endliche Menge an zusammenhängenden Seitenabfolgen dar. Das heißt im Falle von myGourmet, dass alle Seiten, welche zur Registrierung eines Accounts erforderlich sind einen Dialog bilden. Ein Dialog kann ordnungsgemäß oder vorzeitig beendet werden. Beide Fälle werden in myGroumet demonstriert. Solang ein(e) BenutzerIn die Registrierung nicht abgeschlossen hat, kann der Dialog über einen Button zum Abbrechen vorzeitig beendet werden. In diesem Fall wird das Customer -Objekt in der Session resettet, ein entsprechendes Ergebnis an die ursprüngliche Seite zurückgeliefert und der Dialog geschlossen. Dies ist in Abbildung Dialoge abbrechen ersichtlich.
    public void cancelRegistration(ActionEvent event)
    {
        setupNewCustomer();

        RequestContext.getCurrentInstance().returnFromDialog( false, null );
    }

    private void setupNewCustomer()
    {
        FacesContext context = FacesContext.getCurrentInstance();
        context.getApplication().createValueBinding( "#{customer}" ).setValue( context, new Customer());
    }

Abbildung: Dialoge abbrechen
Wird der Dialog hingegen ordnungsgemäß abgeschlossen, so wird der/die BenutzerIn registriert und dies wird ebenfalls an die ursprüngliche Seite zurückgemeldet. Der entsprechende Listener wird in Abbildung Werte an Dialoge übergeben und von Dialogen zurückgeben dargestellt. Im ReturnListener wird eine Property des Customer -Objekts gesetzt. Natürlich könnte dies bereits im ActionListener des letzten Buttons im Registrierungsdialogs gemacht werden. Allerdings soll hier lediglich die Verwendung des ReturnListeners veranschaulicht werden.
Auf der Seite home.xhtml wird abhängig vom Ergebnis ein passender Text angezeigt. Dies wird ebenfalls über PPR realisiert, da auch diese Dialog-Events einen PPR Request anstoßen können. Entsprechend wird die ID des Buttons, welcher den Dialog startet, der Textkomponente im Attribut partialTriggers hinzugefügt. Abbildung Werte an Dialoge übergeben und von Dialogen zurückgeben zeigt die entsprechende Textkomponente.
Die Attribute des tr:commandButton für die Dialog-Listener heißen launchListener und returnListener und sind ebenfalls in Abbildung Werte an Dialoge übergeben und von Dialogen zurückgeben dargestellt.
        <tr:outputText
            value="#{registrationPageBean.customer.loggedIn == true ? messages.registration_finished : messages.not_registered}"
            partialTriggers="defaultButton"/>
        <tr:commandButton id="defaultButton"
                          text="#{messages.register}"
                          action="#{nav.register}"
                          immediate="true"
                          useWindow="true"
                          partialSubmit="true"
                          windowWidth="700"
                          windowHeight="500"
                          launchListener="#{registrationPageBean.launchRegistrationDialog}"
                          returnListener="#{registrationPageBean.returnRegistrationDialog}"/>
    public void launchRegistrationDialog(LaunchEvent event)
    {
        setupNewCustomer();
    }

    public void returnRegistrationDialog(ReturnEvent event)
    {
        Object returnValue = event.getReturnValue();

        if( returnValue == null || (Boolean)returnValue == false)
        {
            setupNewCustomer();
        }
        else
        {
            this.customer.setLoggedIn( true );
        }
    }
Abbildung: Werte an Dialoge übergeben und von Dialogen zurückgeben
Die Prüfung auf null im ReturnListener ist wichtig, da dies zutrifft, wenn der Dialog nicht über die Schaltflächen zum Abbrechen geschossen wurde, sondern über das Schließen Symbol vom Dialog in der rechten oberen Ecke.

<tr:commandButton id="defaultButton" text="#{messages.finish}" actionListener="#{registrationPageBean.finishRegistration}"/>
    public void finishRegistration(ActionEvent event)
    {
        RequestContext.getCurrentInstance().returnFromDialog( true, null );
    }
Abbildung: Ergebnisse eines Dialogs
Der ActionListener finishRegistration aus Abbildung Ergebnisse eines Dialogs setzt das Ergebnis des Dialogs. Dieses Ergebnis wird im ReturnListener aus Abbildung Werte an Dialoge übergeben und von Dialogen zurückgeben abgeholt und weiter verarbeitet. Neben einem einzelnen Rückgabewert kann auch eine Map mit Parametern zurückgegeben werden. In beiden fällen werden die Daten intern im Session Scope abgelegt. Sollen an den Dialog Parameter übergeben werden, so kann dies im LaunchListener über event.getDialogParameters() gemacht werden. Auf die Parameter-Map kann über event.getReturnParameters() zugegriffen werden.

Hinweis:
Außerdem wurde in der Klasse RegistrationPageBean das address -Property umgestellt. In step tr03 wird hier nicht mehr direkt eine Instanz erzeugt, sondern es wird eine Instanz über die managed Properties Funktionalität injected. Die Funktionalität ändert sich hingegen nicht.

11.6 PageFlowScope in Dialogabläufen

Welcher Scope für Beans gewählt werden soll, ist eine häufig gestellte Frage. Gerade bei Dialogabläufen ist der Request Scope zu kurz und der Session Scope zu lange. Mittlerweile gibt es viele Möglichkeiten Scopes einzusetzen, welche länger als der Request Scope sind, jedoch kürzer als der Session Scope. Trinidad stellt hierfür ebenfalls einen Scope namens PageFlowScope zur Verfügung. Dieser Scope kann entweder im Source Code manuell verwaltet werden, oder es wird ein spezieller ActionListener eingebunden.

In step tr03 wurde bereits eine Möglichkeit implementiert, wie der Request Scope verlängert werden kann. Für die Bearbeitung von Adressen wird in tr03 das Tag tr:setActionListener verwendet. Mit diesem speziellen ActionListener kann ein Wert kopiert werden. Hierbei gibt es zwei Möglichkeiten. Im Beispiel wird der Wert genau um einen Request verlängert, da der Wert in ein Bean im Request Scope kopiert wird. Die Implementierung in myGourmet ist in Abbildung Verwendung von tr:setActionListener ersichtlich.

<tr:column>
    <tr:commandLink text="#{messages.edit}" action="#{nav.editAddress}" immediate="true"
                    actionListener="#{registrationPageBean.deleteAddress}">
        <tr:setActionListener from="#{entry}" to="#{registrationPageBean.address}"/>
    </tr:commandLink>
</tr:column>
Abbildung: Verwendung von tr:setActionListener
Durch diesen Mechanismus wird das Objekt im darauf folgenden Lifecycle-Durchlauf verfügbar. Die zweite Möglichkeit wird in myGourmet nicht verwendet und ermöglicht Werte länger als für die nächste Seite zu speichern. Hierfür stellt Trinidad eine Konvention zur Verfügung. Durch das Prefix " pageFlowScope" wird ein Objekt in diesem Scope verfügbar. Das nachfolgende Beispiel zeigt, wie die Map dieses Scopes manuell im Source Code erreichbar ist.

RequestContext.getCurrentInstance().getPageFlowScope()

Der Nachteil dieser Scope Implementierung ist, dass die Objekte so lange im Scope existieren, bis dieser manuell beendet wird. Folgender Ausschnitt zeigt den erfolgreichen Aufruf.

RequestContext.getCurrentInstance().getPageFlowScope().clear();

Wird dies nicht gemacht, so gibt es keinen Unterschied zum Session Scope. Weitere Nachteile sind:
Hinweis:
Im Abschnitt ATC wird ein Bestelldialog implementiert, welcher die bisher beschriebenen Mechanismen verwendet. Zur besseren Übersicht bekommen alle Seiten, welche zu einem Dialog gehören einen eigenen Prefix im Seitennamen. Die Seiten für den Registrierungsdialog bekommen den Prefix " crd" (customer registration dialog) und die Seiten für des Bestelldialogs den Prefix " pod " (product order dialog).

11.7 Clientseitige Konvertierung und Validierung

Standardmäßig konvertiert und validiert JSF serverseitig. Das heißt, dass bei jeder falschen Eingabe ein Request zum Server verursacht wird. Schlägt die Konvertierung oder Validierung fehl, so wird die Seite wieder an den Client gerendert und sofern dies festgelegt wurde, wird eine Fehlermeldung angezeigt. Trinidad ermöglicht es mit clientseitiger Konvertierung und Validierung, dass dieser Server-Request entfällt, wenn Konvertierung oder Validierung fehl schlägt. Hierzu können entweder Trinidad Komponenten mit clientseitigen Mechanismen verwendet werden oder es muss entsprechende Logik in JavaScript implementiert werden.

Eine häufige Anforderung ist die Validierung von E-Mail Adressen. In der ersten Dialogseite der Kundenregistrierung ist die Eingabe der E-Mail Adresse erforderlich. Bisher konnte jeder beliebige Wert eingegeben werden. Die Validierung einer E-Mail Adresse kann bspw. mit Hilfe einer Regular-Expression umgesetzt werden. Hierzu bietet Trinidad eine passende Komponente, welche zusätzlich die Validierung clientseitig übernimmt. Abbildung Beispiel für Komponenten mit clientside validation zeigt die Änderungen in der Seite.

<tr:inputText label="#{messages.email}" value="#{registrationPageBean.customer.email}" required="true">
    <tr:validateRegExp pattern="#{registrationPageBean.emailRegExp}" messageDetailNoMatch="#{messages.no_valid_email}"/>
</tr:inputText>
Abbildung: Beispiel für Komponenten mit clientside validation
Die Methode getEmailRegExp liefert eine entsprechende Regular-Expression zurück. Damit im Fehlerfall keine unleserliche Regular-Expression im Fehlertext angezeigt wird, kann ein eigener Fehlertext zur Verfügung gestellt werden. Äquivalent wird in step tr04 der restliche Registerungsdialog mit entsprechenden Tags zur Validierung ausgestattet. Hierbei geht es nicht darum möglichst reale Validierungen zu zeigen, sondern um die Verwendung möglichst vieler Validierungskomponenten. Beispielsweise kann man den Vor- und Nachnamen ebenfalls durch eine Regular-Expression validieren. Die Regular-Expression selbst ist hier nur von der Format-Vorgabe selbst abhängig. Weitere Validierungskomponenten werden im Laufe des Kapitels ATC verwendet. In der Grundsätzlichen Verwendung besteht allerdings kein Unterschied. Bei allen Komponenten wird durch das Einbinden der Komponente in die Seite automatisch clientseitig validiert.

Außerdem wird bei einem Validierungsfehler neben dem Label des Eingabefelds standardmäßig ein rotes X platziert. Zusätzlich wird unterhalb des Eingabefelds ein entsprechender Fehlertext angezeigt.

Zusätzlich zu den out-of-the-box Mechanismen für clientseitige Konvertierung und Validierung ist es möglich eigene JavaScript Implementierungen zu nutzen. In der Beispielapplikation wird in step tr04 ein eigener Validator für die Kategorien implementiert. Inhalt der gezeigten Implementierung ist, dass der Eintrag " keine" nur alleine, also in keiner Kombination mit anderen Einträgen, vorkommen darf.

Hinweis:
Die clientseitige Validierung von Multi-Select Komponenten ist ab Trinidad 1.x.5 möglich. Der entsprechende Patch steht unter https://issues.apache.org/jira/browse/TRINIDAD-860 zur Verfügung.

In bisherigen Kapiteln wurden die serverseitige Validierung demonstriert. Abbildung Beispiel für einen konventionellen Validator zeigt eine mögliche Implementierung der zuvor beschriebenen Anforderungen.
public class CategoryValidator implements Validator
{
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
    {
        if(value == null || !( value instanceof List ))
        {
            return;
        }

        List categories = (List)value;

        if(categories.size() > 1 && categories.contains( "-1" ))
        {
            ResourceBundle messages = ResourceBundle.getBundle( "at.irian.jsfbook.messages",
                                        FacesContext.getCurrentInstance().getViewRoot().getLocale() );
            throw new ValidatorException( new FacesMessage( messages.getString( "category_validator_details" ) ) );
        }
    }
}
Abbildung: Beispiel für einen konventionellen Validator
Dieser Validator wird in Abbildung Beispiel für einen clientside Validator - die Java Seite auf clientseitige Validierung vorbereitet.

public class CategoryValidator implements Validator, ClientValidator
{
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
    {
        if (value == null || !( value instanceof List ))
        {
            return;
        }

        List categories = (List) value;

        if (categories.size() > 1 && categories.contains( "-1" ))
        {
            ResourceBundle messages = ResourceBundle.getBundle( "at.irian.jsfbook.messages",
                                        FacesContext.getCurrentInstance().getViewRoot().getLocale() );
            throw new ValidatorException( new FacesMessage( messages.getString( "category_validator_details" ) ) );
        }
    }

    public String getClientLibrarySource(FacesContext context)
    {
        return FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath() + "/jsLibs/categoryValidator.js";
    }

    public Collection<String> getClientImportNames()
    {
        return null;
    }

    public String getClientScript(FacesContext context, UIComponent component)
    {
        return null;
    }

    public String getClientValidation(FacesContext context, UIComponent component)
    {
        ResourceBundle messages = ResourceBundle.getBundle( "at.irian.jsfbook.messages", FacesContext.getCurrentInstance().getViewRoot().getLocale() );

        return "new TrCategoryValidator({" + "summary:'" + messages.getString( "category_validator_summary" ) + "'," + "details:'" + messages.getString( "category_validator_details" ) + "'})";
    }
}
Abbildung: Beispiel für einen clientside Validator - die Java Seite
Abbildung Beispiel für einen clientside Validator - die Java Seite zeigt die Implementierung eines internationalisierten Validators. Die Methode getClientValidation ist dafür verantwortlich einen String zurückzugeben, welcher zur Instantiierung des JavaScript-Validators erforderlich ist.
Die Entsprechende JavaScript Implementierung ist in Abbildung Beispiel für einen clientside Validator - die JavaScript Seite dargestellt.
function TrCategoryValidator(messages)
{
    this.messages = messages;
}

function validateCategory(value, label, converter)
{
    if(value.length > 1)
    {
        for(var i = 0; i < value.length; i++)
        {
            if(value[i] == "-1")
            {
                var facesMessage = new TrFacesMessage(
                              this.messages[TrCategoryValidator.SUMMARY],
                              this.messages[TrCategoryValidator.DETAILS],
                              TrFacesMessage.SEVERITY_ERROR);

                throw new TrValidatorException(facesMessage);
            }
        }
    }
}

TrCategoryValidator.prototype = new TrValidator();
TrCategoryValidator.prototype.validate = validateCategory;
TrCategoryValidator.SUMMARY = 'summary';
TrCategoryValidator.DETAILS = 'details';

Abbildung: Beispiel für einen clientside Validator - die JavaScript Seite