Pass-Through-Attribute über Tags
 
  Pass-Through-Attribute aus Bean-Eigenschaft
 
  Modus xhtml für Facelets-Dateien
 
  Beispiel mit Pass-Through-Elementen
 
  Beispiel mit Pass-Through-Attributen
 
  Bean-Eigenschaft für Pass-Through-Attribute

8 JSF und HTML5

HTML5 ist zurzeit in aller Munde und sicher eines der am häufigsten verwendeten und missbrauchten Modewörter im Bereich der Webentwicklung. Aber hinter dem Hype verbergen sich eine ganze Reihe nützlicher Features und Erweiterungen, die das Leben von Webentwicklern einfacher (und aufregender) machen. Höchste Zeit für JSF auf diesen rasenden Zug aufzuspringen.
Es hat zwar bereits in der Vergangenheit einige Ansätze zur Integration von HTML5 in JSF gegeben, die offizielle Variante ist aber erst seit JSF 2.2 Teil der Spezifikation. Unter dem Oberbegriff "HTML5 Friendly Markup" versammelt JSF zwei grundlegend neue Konzepte: Pass-Through-Attribute und Pass-Through-Elemente.
Beginnen werden wir dieses Kapitel allerdings mit einem kur-zen Abstecher zu den verschiedenen Verarbeitungsmodi für Facelets-Dateien in Abschnitt [Sektion:  Verarbeitungsmodi für Facelets-Dateien] . Danach zeigen wir Ihnen in Abschnitt [Sektion:  HTML5 Pass-Through-Attribute] aber auch schon die Pass-Through-Attribute und in Abschnitt [Sektion:  HTML5 Pass-Through-Elemente] die Pass-Through-Elemente. Abschnitt [Sektion:  MyGourmet 15: HTML5] beschreibt abschließend noch eine Variante von MyGourmet mit den zuvor vorgestellten Konzepten.

8.1 Verarbeitungsmodi für Facelets-Dateien

Als erste und einfachste Maßnahme zur Unterstützung von HTML5 verarbeitet JSF 2.2 Facelets-Dateien etwas anders als JSF 2.1. Im Vergleich zur Vorgängerversion rendert JSF 2.2 immer den HTML5-Doctype <!DOCTYPE html> - unabhängig davon, welcher Doctype in der XHTML-Datei angegeben ist.
Dazu gibt es seit JSF 2.1 die Möglichkeit, Facelets-Dateien in verschiedenen Modi zu verarbeiten. In JSF 2.2 ist das standardmäßig der Modus html5 . Wenn Sie den HTML5-Doctype nicht verwenden wollen, können Sie den Verarbeitungsmodus in der faces-config.xml wie in Listing Modus xhtml für Facelets-Dateien auf den Modus xhtml setzen (Standardmodus in JSF 2.1).
<faces-config-extension>
  <facelets-processing>
    <file-extension>.xhtml</file-extension>
    <process-as>xhtml</process-as>
  </facelets-processing>
</faces-config-extension>
Zusätzlich definiert JSF noch die Modi xml zur Verarbeitung von Facelets-Dateien im XML-Modus (XML-Deklaration, Doctype und Kommentare werden entfernt) und jspx zum Umstieg von JSP.

8.2 HTML5 Pass-Through-Attribute

HTML5 definiert eine ganze Reihe neuer Attribute und Attributwerte für bereits existierende HTML-Elemente. JSF 2.2 liefert mit den sogenannten Pass-Through-Attributen das passende Konzept zur Unterstützung dieser Attribute. Das Prinzip ist einfach: Jede Komponente kann neben den "normalen" Attributen noch eine beliebige Anzahl von Pass-Through-Attributen haben. Diese Attribute werden unverändert an das von der Komponente gerenderte Element weitergereicht und von der Komponente selbst nicht weiterverarbeitet.
Der Vorteil dieses Ansatzes liegt klar auf der Hand. JSF kann damit neue und geänderte HTML5-Attribute unterstützen, ohne die existierenden Standardkomponenten zu ändern. Die JSF-Spezifikation ist somit auch nicht von der HTML5-Spezifikation abhängig. Ein wichtiger Punkt - besonders wenn man bedenkt, dass die Arbeit an HTML5 noch nicht abgeschlossen ist.
JSF definiert mehrere Varianten zur Definition von Pass-Through-Attributen. Die einfachste besteht darin, das Attribut im Namensraum http://xmlns.jcp.org/jsf/passthrough zu definieren. Dazu muss das Attribut lediglich mit dem Präfix des Namensraums versehen werden. Das Beispiel in Listing Beispiel mit Pass-Through-Attributen zeigt eine h:inputText -Komponente mit den beiden regulären Attributen id und value und den Pass-Through-Attributen type und placeholder .
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">
<h:head><title>JSF 2.2 HTML5</title></h:head>
<h:body>
  <h:form id="form">
    <h:inputText id="email" value="#{bean.email}"
        pt:type="email" pt:placeholder="E-Mail eingeben"/>
    <h:commandButton action="#{bean.save}" value="Save"/>
  </h:form>
</h:body>
</html>
Bei den Pass-Through-Attributen handelt es sich um HTML5-Erweiterungen für das Element input , die von h:inputText nicht direkt unterstützt werden. Mit placeholder kann ein Text angegeben werden, der im Browser angezeigt wird, solange der Benutzer noch nichts eingegeben hat. Im Attribut type kommt mit email einer der neuen Typen für Eingabefelder zum Einsatz. Der gerenderte HTML-Code für das h:inputText -Tag beinhaltet wie erwartet alle vier Attribute:
<input id="form:email" name="form:email" value=""
    placeholder="E-Mail eingeben" type="email"/>
Tabelle tab:html5-input-ex zeigt einige Beispiele, wie das input -Element mit den zusätzlichen HTML5-Attributen im Browser dargestellt wird. Die Validierung der Mailadresse wird dabei vom Browser clientseitig beim Submit des Formulars durchgeführt - JSF bekommt davon nichts mit. Aber Achtung: Nicht alle HTML5-Features werden von allen Browsern gleichermaßen unterstützt Eine aktuelle Übersicht, welche Browserversionen welche HTML5-Features unterstützen, finden Sie zum Beispiel unter http://caniuse.com.: .
AnwendungsfallBeispiel
Leeres Eingabefeld
Ungültige E-Email (Chrome 28)
Ungültige E-Mail (Firefox 23)
Neben der Variante mit dem Namensraum können Sie Pass-Through-Attribute auch mit dem Tag f:passThroughAttribute definieren. Der Name des Pass-Through-Attributs wird dabei in name und der Wert in value angegeben, wie Listing Pass-Through-Attribute über Tags zeigt. Die gerenderte Ausgabe ändert sich dadurch nicht.
<h:inputText id="email" value="#{bean.email}">
  <f:passThroughAttribute name="type" value="email"/>
  <f:passThroughAttribute name="placeholder"
      value="E-Mail eingeben"/>
</h:inputText>
Wenn Ihnen diese beiden Varianten noch nicht reichen, können Sie Pass-Through-Attribute mit dem Tag f:passThroughAttributes auch aus einer Bean-Eigenschaft mit dem Typ Map<String, Object> holen, wie Listing Pass-Through-Attribute aus Bean-Eigenschaft zeigt.
<h:inputText id="email" value="#{bean.email}">
  <f:passThroughAttributes value="#{bean.attributes}"/>
</h:inputText>
Listing Bean-Eigenschaft für Pass-Through-Attribute zeigt die zu Listing Pass-Through-Attribute aus Bean-Eigenschaft passende Bean-Eigenschaft.
public Map<String, Object> getAttributes() {
  Map<String, Object> attrs = new HashMap<String, Object>();
  attrs.put("placeholder", "E-Mail eingeben");
  attrs.put("type", "email");
  return attrs;
}
Pass-Through-Attribute ermöglichen auch den Einsatz von HTML5-Custom-Data-Attributen Details zu HTML5-Custom-Data-Attributen finden Sie zum Beispiel unter http://html5doctor.com/html5-custom-data-attributes.: . Diese Attribute sind dazu gedacht, HTML-Elemente mit zusätzlichen Daten zu versehen. Man erkennt sie sofort an ihrem Namen, der immer mit dem Präfix data- beginnt. Custom-Data-Attribute werden im DOM abgelegt und stehen dort für die Verarbeitung mit JavaScript zur Verfügung.
Folgendes Beispiel zeigt ein h:inputText -Tag mit dem Custom-Data-Attribut data-required in Form eines Pass-Through-Attributs.
<h:inputText id="email" value="#{bean.email}"
    pt:type="email" pt:data-required="true"/>
Dieses Attribut hat keine spezielle Bedeutung und wird weder von JSF noch vom Browser gesondert behandelt. Es kann allerdings im JavaScript-Code der Applikation beispielsweise zur clientseitigen Validierung verwendet werden.

8.3 HTML5 Pass-Through-Elemente

Traditionell bauen JSF-Entwickler die Seiten einer Anwendung überwiegend mit JSF-Tags auf und kommen nur indirekt mit den gerenderten HTML-Elementen in Kontakt. Mit dem vermehrten Einsatz von clientseitigen Technologien wie JavaScript ist es allerdings immer öfter von Bedeutung, die Struktur der Seite im Detail zu kennen. JSF 2.2 bietet mit den sogenannten Pass-Through-Elementen eine Lösung für dieses Problem an.
Bei Pass-Through-Elementen handelt es sich um HTML-Elemente, die mit passenden JSF-Komponenten im Komponentenbaum verbunden sind. Ein HTML-Element wird per Definition genau dann zu einem Pass-Through-Element, wenn mindestens eines seiner Attribute im Namensraum http://xmlns.jcp.org/jsf definiert ist.
Listing Beispiel mit Pass-Through-Elementen zeigt nochmals das Beispiel aus Listing Beispiel mit Pass-Through-Attributen . Bei dieser Version wurden allerdings alle JSF-Tags durch Pass-Through-Elemente ersetzt. An der Funktionalität ändert sich dadurch nichts und selbst der Komponentenbaum am Server hat sich nicht entscheidend verändert.
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:jsf="http://xmlns.jcp.org/jsf">
<head jsf:id="head"><title>JSF 2.2 HTML5</title></head>
<body jsf:id="body">
  <form jsf:id="form">
    <input type="text" placeholder="E-Mail eingeben"
        jsf:id="email" jsf:value="#{bean.email}"/>
    <button jsf:action="#{bean.save}">Save</button>
  </form>
</body>
</html>
Wie funktioniert das? Wenn mindestens ein Attribut eines HTML-Elements im Namensraum http://xmlns.jcp.org/jsf definiert ist, transformiert JSF dieses HTML-Element in ein JSF-Tag und fügt die entsprechende Komponente in den Komponentenbaum ein. Die Entscheidung, welches JSF-Tag verwendet wird, fällt JSF anhand des Elementnamens. Ergibt sich daraus keine eindeutige Zuordnung, wird in einigen Fällen zusätzlich der Wert eines Attributs in die Entscheidung mit einbezogen.
Sehen wir uns das anhand des Beispiels aus Listing Beispiel mit Pass-Through-Elementen etwas genauer an. Da das Element head mit id ein Attribut im Namensraum http://xmlns.jcp.org/jsf hat, wird es als Pass-Through-Element behandelt. JSF transformiert das Element head daher in das JSF-Tag h:head und setzt dessen Attribut id auf den angegebenen Wert. Analog wird das Element body in h:body und form in h:form transformiert.
Das Element input lässt sich nur unter Berücksichtigung des Attributs type eindeutig transformieren. Aus der Kombination input und type="text" macht JSF das Tag h:inputText . Werfen wir nochmal einen Blick auf die Attribute von input . Sowohl id als auch value haben ein Präfix und werden somit direkt an die Komponente weitergereicht. Nachdem das HTML5-Attribut placeholder aber kein Präfix besitzt, wird es nicht als normales Attribut, sondern als Pass-Through-Attribut zu h:inputText hinzugefügt. Anders wäre es auch nicht möglich, da h:inputText kein Attribut mit dem Namen placeholder definiert. Anhand dieses Beispiels lässt sich schön erkennen, wie sich Pass-Through-Elemente und Pass-Through-Attribute gegenseitig ergänzen.
Tabelle tab:html5-pte-mapping zeigt eine komplette Liste der in JSF 2.2 definierten Zuordnungen von Element-Attribut-Kombinationen zu JSF-Tags. Attribute mit dem Präfix jsf in der zweiten Spalte bezeichnen Attribute im Namensraum http://xmlns.jcp.org/jsf . Ein Stern wiederum bedeutet, dass der Wert des Attributs für die Zuordnung nicht relevant ist.
HTML-ElementSelektor-AttributJSF-Tag
a jsf:action h:commandLink
a jsf:actionListener h:commandLink
a jsf:value h:outputLink
a jsf:outcome h:link
body h:body
button h:commandButton
button jsf:outcome h:button
form h:form
head h:head
img h:graphicImage
input type="button" h:commandButton
input type="checkbox" h:selectBooleanCheckbox
input type="color" h:inputText
input type="date" h:inputText
input type="datetime" h:inputText
input type="datetime-local" h:inputText
input type="email" h:inputText
input type="month" h:inputText
input type="number" h:inputText
input type="range" h:inputText
input type="search" h:inputText
input type="time" h:inputText
input type="url" h:inputText
input type="week" h:inputText
input type="file" h:inputFile
input type="hidden" h:inputHidden
input type="password" h:inputSecret
input type="reset" h:commandButton
input type="submit" h:commandButton
input type="*" h:inputText
label h:outputLabel
link h:outputStylesheet
script h:outputScript
select multiple="*" h:selectManyListbox
select h:selectOneListbox
textarea h:inputTextArea
Nachdem Pass-Through-Elemente in JSF-Tags transformiert werden, können sie auch wie JSF-Tags behandelt werden. Im folgenden Codefragment wird zum Beispiel ein Validator direkt mit f:validateLength zum HTML-Element hinzugefügt. Das sieht zugegebenermaßen auf den ersten Blick etwas befremdlich aus, funktioniert aber einwandfrei.
<input type="text" jsf:value="#{bean.name}">
  <f:validateLength minimum="3"/>
</input>
Im nächsten Beispiel wird das Element a mit dem Attribut jsf:outcome in ein h:link -Tag transformiert. Daher ist es auch möglich, die ID mit f:param als Parameter für den gerenderten Link zu definieren.
<a jsf:outcome="details" title="Show #{person.name}">
  #{person.name}
  <f:param name="id" value="#{person.id}"/>
</a>
Passen Sie genau auf, welche Attribute von Pass-Through-Elementen Sie im Namensraum http://xmlns.jcp.org/jsf definieren. Im nächsten Beispiel ist das Attribut value ohne Namensraum definiert und wird als Pass-Through-Attribut an die Komponente weitergereicht - ein großer Unterschied, wie wir gleich sehen werden.
<input jsf:id="name" type="text" value="#{bean.name}">
  <f:validateLength minimum="3"/>
</input>
Auf den ersten Blick funktioniert das Beispiel ohne Probleme. Der gerenderte Wert wird aber nicht von der Komponente verwaltet, sondern immer direkt aus der Value-Expression in die gerenderte Ausgabe geschrieben. Bei einem Validierungsfehler bekommt der Benutzer dadurch aber wieder den alten Wert und nicht den Submitted-Value zu sehen.
Tipp: Definieren Sie bei Pass-Through-Elementen unbedingt alle Attribute, die zur Komponente gehören, im Namensraum http://xmlns.jcp.org/jsf .
JSF kann auch mit Pass-Through-Elementen umgehen, für die keine fixe Zuordnung zu einem JSF-Tag definiert ist. In diesem Fall wird eine generische Komponente in den Komponentenbaum eingefügt. Damit ist es auch möglich, HTML5-Elemente wie progress zu Pass-Through-Elementen zu machen und sie sogar mit Ajax-Verhalten auszustatten, wie das folgende Beispiel zeigt. Jeder Klick auf das progress -Element löst einen Ajax-Request aus und rendert die ihm zugeordnete Komponente neu.
<button jsf:action="#{bean.update}">
  Aktualisieren
  <f:ajax render="progress"/>
</button>
<progress jsf:id="progress" max="10" value="#{bean.progress}">
  <f:ajax event="click" render="@this"/>
</progress>

8.4 MyGourmet 15: HTML5

Bei MyGourmet 15 handelt es sich um eine erweiterte Variante von MyGourmet 14 , die an einigen Stellen mit Pass-Through-Attributen und Pass-Through-Elementen erweitert wurde.
In editAddress.xhtml haben zum Beispiel alle Eingabefelder das HTML5-Attribut placeholder über f:passThroughAttribute bekommen. In editProvider.xhtml hingegen wurde placeholder direkt als Attribut im Pass-Through-Namensraum hinzugefügt. Des Weiteren wurde die Kompositkomponente mc:inputSpinner durch ein Eingabefeld mit dem neuen HTML5-Typ number ersetzt, wie das folgende Beispiel zeigt.
<h:inputText pt:type="number" pt:min="0" pt:step="1"
    value="#{providerBean.provider.stars}"
    pt:placeholder="#{msgs.enter_stars}"/>
Wenn der Browser diesen Typ unterstützt, wird dadurch nativ ein Eingabefeld für Zahlen mit Schaltflächen zum Erhöhen und Reduzieren des Werts angezeigt.
In editCustomer.xhtml wurden soweit möglich alle JSF-Tags durch Pass-Through-Elemente ersetzt. Zusätzlich kommt beim Eingabefeld für die Mailadresse der HTML5-Typ email zum Einsatz.