Das Produkt JSF Ext enthält eine Reihe von JSF-Tags. Diese Seite beschriebt die Anwendung der Tags.

Ausnahmen:

  • Die <e:push> und <e:socket> für AJAX-Push werden in JSF Ext Push beschrieben.
  • Die Tags <e:load>, <e:unload>, <e:scope> und <e:scopes> werden bei JSF Ext Scopes beschrieben.

Inhalt#

Control Tags#

In diesem Abschnitt werden Tags für den Umgang mit Tag Handling und Aufbau der Page beschrieben.

Insert Tag#

Der Insert Tag kann Components in den Component Tree einfügen, anhand einer EL-Expression. Dies ist unter anderem nützlich, wenn man Composite Components baut. Also Components mit dem Namespace http://java.sun.com/jsf/composite/...

In der Composite Component gibt es nur den Tag <cc:insertChildren>, damit hat man keine detailierte Kontrolle, die Children werden alle an derselben Stelle eingefügt. Möchte man diese zum Beispiel durch ein SPAN-Tag wrappen, braucht man den Insert Tag.

Lösung: Man iteriert durch die Children mittels <c:forEach> und fügt diese an einer beliebigen Stelle mittel <e:insert> ein.

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:cc="http://java.sun.com/jsf/composite"
	xmlns:e="http://java.sun.com/jsf/ext"
	xmlns:ext="http://java.sun.com/jsf/composite/ext"
>
	<cc:interface>
		<cc:attribute name="align" default="center"/>
	</cc:interface>
	<cc:implementation>
		<div style="margin-top: 5px; text-align: #{cc.attrs.align};">
			<c:forEach items="#{cc.children}" var="child">
				<h:panelGroup style="padding: 5px 5px 0 0;" rendered="#{child.rendered}">
					<e:insert component="#{child}"/>
				</h:panelGroup>
			</c:forEach>
		</div>
	</cc:implementation>
</html>

JavaScript ausgeben#

Behavior Tag#

Eine Neuerung bei JSF2 ist die Einführung von Behaviors, also das Generieren von Java-Script-Snippets aus Java heraus. Ein Behavior kann jeder Komponente hinzugefügt werden, die ClientBehaviorHolder implementiert.

In vielen Fällen hab man bereits Behavior-Components zu einem Tag hinzugefügt. Würde man dennoch das entsprechende Attribut verwenden (z.B. onclick bei commandButton), kann es zu Problemen kommen, wie die Reihenfolge der Snippets oder das "return false;"-Anhängsel.

Lösung bringt der Behavior-Tag von JSF Ext. Typisch ist zum Beispiel die Verwendung zusammen mit dem AJAX-Tag:

<h:commandButton value="X">
	<f:ajax/>
	<e:unload/>
	<e:behavior script="#{rich:component('popup-panel')}.hide(event);"/>
</h:commandButton>

Die Behavior-Snippets werden in der entsprechenden Reihenfolge produziert:

jsf.util.chain(this,event,'mojarra.ab(this,event,\'action\',0,0)','RichFaces.$(\'popup:form:popup-panel\').hide(event);');return false

Evaluate Tag#

Wenn mit Actions und Java-Script gearbeitet wird, kann eine Ausführung im Erfolgsfall des Form Submit sinnvoll sein. Dies kann durch den Evaluate-Tag erreicht werden, der einen ActionListener darstellt und entsprechendes Script ausführen kann.
<h:commandButton value="Submit">
    <f:ajax/>
    <e:evaluate script="alert('Success');"/>
</h:commandButton>

Zusätzlich ist der Evaluate-Tag ein ClientBahaviorHolder, sodass Behavior-Tags verwendet werden können, um das Script zu erzeugen.

Script Tag#

Der Tag <e:script> gibt Scripte beim AJAX-Rendering aus. Es ist keine Kopplung an Behaviors oder Actions erforderlich. Dadurch können Aktionen beim Rendern von Components aufgeführt werden, zum Beispiel ein Highlighting durch JavaScript:
<e:script script="ext.util.flash('some-tag', '#80ff80');"/>

Output Behavior#

Der Tag <e:outputBehavior> gibt das Script anderer Behaviors als Text aus. Dies ist vor allem in Composite-Tags nützlich, die Behaviors unterstützen sollen. Dies ist vor allem bei AJAX-Request nützlich, weil es viel bequemer und robuster ist <f:ajax>, <e:ajax> oder dergleichen zu benutzen anstatt von jsf.ajax.request oder anderen JavaScript-Konstrukten.

Möchte man zum Beispiel einen Composite-Tag schreiben, der am unteren Ende einer Page ein Behavior ausführt, kann das wie folgt geschehen:

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
	xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:cc="http://java.sun.com/jsf/composite"
	xmlns:e="http://java.sun.com/jsf/ext"
>
	<cc:interface>
		<cc:clientBehavior name="behavior" event="behavior" targets="behavior" default="true"/>
	</cc:interface>
	<cc:implementation>
		<h:outputScript name="util.js" library="ext-js"/>
		<script id="#{cc.clientId}" type="text/javascript">
			ext.util.addListener(window, 'scroll', function(event) {
				var body = event.target.body;
				var bottom = body.scrollTop + window.innerHeight;
				if (bottom == body.scrollHeight) {
					<e:outputBehavior id="behavior"/>
				}
			});
		</script>
	</cc:implementation>
</html>

Erklärung: Der Tag definiert das Behavior "behavior" als Behavior-Parameter. Das übergebene Behavior wird dann an der Stelle des <e:outputBehavior> im JavaScript-Block ausgegeben.

Hinweis: Dieser Tag ist als <ext:onscroll> bereits in JSF Ext enthalten.

Unterschiede der Tags#

Unterschied zwischen Behavior-, Evaluate- und Script-Tag:

BehaviorEvaluateScript
Zeitpunkt der AusführungDirekt durch den Browser-EventDurch den AJAX-Request an den Browser gesendetDurch das AJAX-Rendering der Component
Bedingungen für die AusführungKeine Server-Bedingungen möglich, nur Java-Script if-StatementsDas Script wird nur nach erfolgreichen Submit ausgeführt, also wenn die Validation erfolgreich warNur beim AJAX-Rendering, nicht beim Aufbau der Seite.
AnwendungszweckSteuerung des Browserverhaltens, Ein- und Ausblenden, Fading, dynamische Browserelemente, Client-BehaviorAbwickeln von Server-States, Popups öffnen und Schließen, Submits bestätigenSpezielle AJAX- und Push-Features, Highlighting, Informationen.

AJAX Tag#

Der AJAX-Tag von JSF Ext erlaubt das Abschicken eines anderen Formulars. Der Tag <f:ajax> kann nur das aktuelle Formular abschicken, innerhalb der Action-Source in der er sich befindet.

Sources#

Manchmal ist es sinnvoll andere Sources abzuschicken, dafür stellt JSF Ext den Tag <e:ajax> zur Verfügung:
<h:form id="test-form">
	<h:panelGrid columns="3">
		<h:outputText value="Name"/>
		<h:inputText id="name" value="#{bean.name}" required="true"/>
		<h:message for="name"/>
	</h:panelGrid>
</h:form>
<h:form id="other-form">
	<h:commandButton id="tag-save" value="Tag Button">
		<e:ajax source=":test-form" render=":test-form"/>
	</h:commandButton>
</h:form>

Der Tag <e:ajax> ersetzt in diesem Fall den Tag <f:ajax>, er braucht nicht zusätzlich angegeben zu werden.

Action Source#

Der Tag kann auch Actions abschicken, wenn er an einfachen ClientBehaviorHolder gehängt wird, weil er selbst eine ActionSource darstellt:
<h:panelGrid>
    <f:ajax event="dblclick" action="#{bean.action}"/>
</h:panelGrid>

Client-Behaviors#

Der Tag <e:ajax> ist nicht nur ein ClientBehavior sonder auch selbst wieder ein ClientBehaviorHolder. Er unterstützt die vier AJAX-Events begin, complete, success und error, wobei das Default-Event success ist:
<h:graphicImage id="edit" value="edit.png">
	<e:ajax event="click">
            <f:setPropertyActionListener value="#{element}" target="#{editController.element}"/>
            <rich:componentControl target=":some-popup" operation="show"/>
	</e:ajax>
</h:commandButton>

Request Parameter#

Der AJAX-Tag kann auch Request-Parameter übertragen.
<h:graphicImage name="process.png" library="images/info">
	<e:ajax event="click" action="/index.xhtml">
		<f:param name="mode" value="AVAILABLE"/>
	</e:ajax>
</h:graphicImage>

Achtung: Die im Value-Attribut verwendeten EL-Expressions werden bereits beim Rendern der View ausgewertet und können möglicher Weise veraltete, nicht gewünschte Werte enthalten. Verwenden sie diese Möglichkeit zur Übergabe von Parametern nur, wenn sie genau wissen, was sie tun.

Hinweis: In den meisten Fällen ist es angebraucht, die Werte mit <e:set> (bzw. <f:setPropertyActionListener>) zu setzen, weil die entsprechenden EL-Expressions erst beim Drücken des Knopfs ausgewertet werden.

DIV-Tag#

In JSF gibt es eine Reihe von Möglichkeiten, einen DIV- oder SPAN-Tag zu erzeugen, zum Beispiel durch <h:panelGroup>. Dabei handelt es sich jedoch um einfache Komponenten, die keine ClientBehaviorHolder sind, und daher kein <f:ajax>, <e:bahavior> und andere Behaviors unterstützen.

Der Tag <e:div> ist ein vollwertiger ClientBehaviorHolder:

	<e:div id="mouse-active" tabindex="0" onkeypress="alert((event || window.event).keyCode);">
		<ext:mouse-focus id="mouse-focus"/>
		<h1>TEST</h1>
	</e:div>

Damit wird das Erstellen von einfachen Komponenten mit wenig HTML-Code und flachen Komponenten-Bäumen deutlich vereinfacht.

Attributes und Paramters#

Attribute Tag#

Bei manchen Tags fehlen Attribute, wie zum Beispiel placeholder oder oncontextmenu. JSF-Tags reichen die Attribute nicht durch, daher besteht keine andere Möglichkeit diese Attribute hinzuzufügen. In den meisten Fällen können solche Attribute durch das Attribute-Tag trotzdem hinzugefügt werden:
    <h:inputText id="name" value="#{bean.name}">
        <e:attribute name="placeholder" value="Name eingeben..."/>
    </h:inputText>

Des Weiteren unterstützt der Attribut-Tag folgende Attribute:

NameBeschreibung
finalizerZeichen für den Abschluss eines Behaviors. Default ist, dieses Zeichen aufgrund einer Reihe von Heuristiken zu erraten. Dazu zählen Behaviors und Attribut-Namen wie z.B. "style" mit den Finalizern ";" und "". Jeder untergeordnete Behavior-, Part- und anderer Tag wird mit diesem Zeichen abgeschlossen.
delimiterZeichen für die Abtrennung untergeordneter Behavior-Tags usw. Im Gegensatz zum Finalizer wird dieses Zeichen nur zwischen den Behaviors eingefügt, nicht jedoch am Ende. Default ist ein einzelnes Whitespace.

Hinweis: Durch dieses Feature können Strings bequem zusammengesetzt werden, die mit Attributen "rendered" versehen sind. Untergeordnete Behaviors, wie das Part-Tag, können die Werte nochmal für einzelne Strings überschreiben.

Attribute-Behaviors#

Der Attribute-Tag ist ein ClientBehaviorHolder, es können also Behaviors darunter gehängt werden wie <e:behavior> oder sogar <e:ajax>. Dadurch ist es möglich, Behaviors für Attribute zu festzulegen, die eigentlich nicht dafür gedacht sind:
<h:panelGroup>
    <e:attribute name="oncontextmenu">
        <f:ajax listener="#{bean.render}" render=":some-menu"/>
    </e:attribute>
</h:panelGroup>

Zusammengesetzte Attribute#

Gelegentlich möchte man Attribute zusammen setzen, zum Beispiel um Styles in Composite-Tags zu rendern. Dies kann durch Kombination eines Attribute-Tags und mehreren Behavior-Tags erreicht werden:
<e:div id="#{cc.id}" styleClass="button-bar">
	<e:attribute name="style" renderEmpty="false">
		<e:behavior script="text-align: #{cc.attrs.align};" rendered="#{!empty cc.attrs.align}"/>
		<e:behavior script="background-color: #{cc.attrs.color};" rendered="#{!empty cc.attrs.color}"/>
	</e:attribute>
	...
</e:div>

Part Attribute#

Zusammengesetzt Attribute können bequem durch <e:part> erzeugt werden:
<h:commandButton>
	<e:attribute name="value">
		<e:part value="This"/>
		<e:part value="is"/>
		<e:part value="a"/>
		<e:part value="composed"/>
		<e:part value="value"/>
	</e:attribute>
</h:commandButton>

Hinweis: Der Part-Tag kann hilfreich sein, wenn bedingte styleClass-Attribute auftreten. Entsprechende Teile können mit dem rendered-Attribut gesteuert werden, anstatt unübersichtliche EL-Expressions zu konstruieren.

Style Attribute#

Da Style-Attribute besonders häufig auftreten und zusammengesetzt werden, gibt es dafür einen eigenen Tag:
<e:div>
    <e:attribute name="style">
	<e:style name="text-align" value="#{valueEdit.permission.field.type.align}"/>
	<e:style name="width" value="100%"/>
	<e:style name="margin-left" value="10px"/>
    </e:attribute>
</e:div>

Method Parameters#

Für Zwecke wie dem Laden eines Scopes mit <e:load> können Parameter mit <f:param> übergeben. Dies unterstützt allerdings nur Value-Expressions und keine Method-Expressions. Mit dem Tag <e:method-param> können auch Methoden übergeben werden. Dies geht allerdings nur, wenn EL 2.0 unterstützt wird.
    <e:load viewId="/dialogs/someDialog.xhtml">
        <e:method-param name="action" value="#{test.action}"/>
    </e:load>

Damit können parameterisierte Dialoge erstellt werden, deren Action-Elemente bestimmte Methoden ausführen.

Erklärung: Der Aufruf findet dann über #{scope.action.invoke} statt. Die eigentliche Methode wird im EL-Scope als Value abgelegt und enthält die Methode "invoke", mit der die Methode dann aufgerufen werden kann.

Hinweis: Der Aufruf kann auch mit Parametern erfolgen, wenn #{scope.action.invoke(param)} verwendet wird.

Soll eine derart übergebene Method-Expression weitergereicht werden, kann dies als normaler <f:param> erfolgen:

<h:commandLink value="Nested">
	<f:ajax/>
	<e:load scopeId="select" viewId="/dialog.xhtml">
		<f:param name="action" value="#{scope.action}"/>
	</e:load>
</h:commandLink>

Utility Tags#

Reference-Tag#

Achtung: Der Reference-Tag wird nicht mehr unterstützt.

Begründung: Der Reference-Tag wird seit längerem durch Scopes ersetzt. Diese sind wesentlich besser in JSF integriert, brauchen weniger Ressourcen und sind flexibler in der Anwendung.

New-Tag#

Der Tag <e:new> kann an jeder Stelle verwendet werden, an der auch <f:param> verwendet wird:
<e:load scopeId="test-scope">
    <e:new name="bean" type="com.intersult.test.Bean"/>
</e:load>

Es ist auch möglich, Properties der neuen Bean Werte zuzuweisen:

<e:new name="bean" type="com.intersult.test.Bean">
    <f:param name="param1" value="#{some-expression}/>
</e:new>

Hinweis: Es ist zu erwähnen, dass das Instantiieren von Beans durch den New-Tag möglicher Weise nicht die beste Praxis ist. Die erste Wahl beim Erzeugen von Beans sollte das Verwenden von Scope- und Factory-Annotations sein, entweder über JSF mit @ManagedBean oder über Spring mit @Component. An zweiter Wahl steht das Erzeugen im Java-Code, zum Beispiel mit Lazy-Initialization Pattern "if (bean == null) bean = new Bean();". <e:new> macht vor allem da Sinn, wo eine neue leere Bean erzeugt werden soll (z.B. Create Popup) und wo mehrere Parameter durch eine Bean übergeben werden sollen.

Set-Tag#

Eine gängige Praxis ist das Setzen von Java-Properties mittels <f:setPropertyActionListener>. Bei Composite Components können ebenfalls als ActionSource agieren:
<?xml version="1.0" encoding="UTF-8"?>
<html 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"
	xmlns:e="http://java.sun.com/jsf/ext"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
	xmlns:cc="http://java.sun.com/jsf/composite"
>
	<cc:interface>
		<cc:attribute name="label"/>
		<cc:attribute name="action" targets="link" method-signature="void action()"/>
		<cc:actionSource name="action" targets="link" default="true"/>
	</cc:interface>
	<cc:implementation>
		<h:commandLink id="link"
		    <h:outputText value="#{cc.attrs.label}"/>
		</h:commandLink>
	</cc:implementation>
</html>

Dieser Tag kann wie folgt verwendet werden:

<app:myLink label="Some Link">
    <e:set value="Some Value" target="#{bean.value}"/>
</app:myLink>

Beim Verwenden von <f:setPropertyActionListener> müsste man explizit mit einem Attribut for="action" die Action spezifizieren. Der Tag <e:set> macht dies automatisch, bei häufigem Verwenden sieht der kurze Name einfach ausdrucksvoller und aufgeräumter aus.

Label#

JSF Ext liefert einen Tag <e:outputLabel> mit, der auf Required-Attribute mit einem unterschiedlichen Style reagiert. Zusätzlich berücksichtigt werden die Annotations @NotNull und @NotEmpty auf den durch EL-Expressions gebundenen Bean-Klassen.

Die Verwendung ist identisch mit <h:outputLabel>:

<e:outputLabel for="username:username" value="#{messages['user.username']}"/>
<ext:input id="username">
	<h:inputText id="username" value="#{userRegister.user.username}"/>
</ext:input>

Dabei kommen die beiden CSS-Style-Klassen label-required und label-not-required zum Einsatz, die mit "font-weight: bold;" und "color: #a0a0a0;" vorbelegt sind. Diese können durch ein Benutzer-Stylesheet überschrieben werden.

Der <e:outputLabel> unterstützt das Attribut disabled, mit dem der Label als optional angezeigt werden kann, unabhängig von der tatsächlichen Required-Eigenschaft. Dies ist nützlich, wenn die zugehörige Input-Component ebenfalls Disabled wird.

Darüber hinaus können die Klassen requiredStyleClass und notRequiredStyleClass auch manuell festgelegt werden, indem diese als Attribut angegeben werden.

Move Listener#

Auf modernen Web-Seiten wird mehr und mehr mit Popups und Elementen gearbeitet, die mit der Maus verschoben werden können. Der Move-Listener ist ein Event-Generator, mit dem solche Eingriffe verarbeitet werden können:
<e:moveListener listener="#{dialog.moveListener}"/>

JSF Ext enthält eine Custom-Scoped Bean "dialog", in der das Ergebnis gespeichert werden kann. Im obigen Beispiel geschieht dies durch listener="#{dialog.moveListener}". Ein entsprechendes Popup kann dann durch position="#{dialog.position}" wieder positioniert werden.

Um einem Primefaces-Dialog einen Listener hinzuzufügen, verwendet man:

<e:moveListener listener="#{dialog.moveListener}"
	targetNode="document.getElementById('#{cc.clientId}:dialog').childNodes[0]"
	height="element.childNodes[1].offsetHeight - 16"
	width="element.childNodes[1].offsetWidth"/>

Init-Tag#

In einigen Situationen ist es hilfreich, wenn eine Bean an einer bestimmten Stelle im Component-Tree initialisiert werden kann. So kann beispielsweise ein HTTP-GET-Parameter verarbeitet werden oder ein Scope initialisiert. Dazu gibt es den Tag <e:init>:
<e:init action="#{testBean.init}"/>

Hinweis: Die Action-Methode kann mehrfach aufgerufen werden, wenn Redirects durchgeführt werden.

Der Tag kann innerhalb eines Scopes verwendet werden, um den Scope zu initialisieren, falls kein Laden mit <e:load> erfolgt ist.

<e:scope id="test" load="true">
    <e:init action="#{testBean.init}"/>
    ...
</e:scope>
public class TestBean {
    @ScopeValue
    private String test;
    
    public void init() {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        String test = externalContext.getRequestParameterMap().get("test");
        if (test != null)
            this.test = test;
    }
    ...
}

Hinweis: Der Load-Tag ist dabei weiterhin möglich.

Der Init-Tag ist eine ActionSource und kann damit auch ActionListener als Unterobjekte haben:

<e:init>
    <e:set value="#{value}" target="#{target}"/>
</e:init>
    

Otherwise-Tag#

Da ist mit dem <c:choose>, <c:when> und <c:otherwise> Konstrukt in JSF Probleme gibt, enthält JSF Ext eine neue Implementierung von Otherweise als Component <e:otherwise>. Diese Component ist einfacher in der Nutzung:
  • Als Basis der Choose-Liste kann eine beliebige Component verwendet werden
  • Child-Components werden mit rendered-Attributen versehen
  • Der Otherwise-Tag wird hinzugefügt und rendered dann, wenn keine der anderen Components gerendert wurde

Das Ganze sieht dann so aus:

<e:div>
    <h:selectBooleanCheckbox id="boolean" value="#{bean.value}" rendered="#{bean.type == 'boolean'}"/>
    ...
    <e:otherwise>
        <h:inputText id="text" value="#{bean.value}"/>
    </e:otherwise>
</e:div>

Async-Tag#

Manchmal kann es von Vorteil sein, wenn der wichtigste Teil einer Seite sofort zur Verfügung steht. Andere Teile können dann nachgeladen werden. Dies trifft insbesondere zu, wenn eine langsame Verbindung besteht oder Teile der Seite mehr Zeit auf dem Server in Anspruch nehmen, etwa bei komplexen Datenbankanfragen oder Berechnungen.

Hier kann der Tag <e:async> verwendet werden, dieser lädt seinen Inhalt erst nach dem Rendern der Seite per AJAX nach:

	<script type="text/javascript">
		jsf.ajaxQueue = 10;
	</script>
	<c:forEach begin="1" end="20" var="index">
		<e:div style="height: 20px; background-color: #e0e0e0; margin-bottom: 3px;">
			<e:async>
				<h:outputText value="Async Content #{index}, sleep = #{async.sleep}"/>
			</e:async>
		</e:div>
	</c:forEach>

Und eine Klasse, die den Request verzögert:

@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class Async {
	private static Random random = new Random();
	
	public int getSleep() throws InterruptedException {
		int sleep = random.nextInt(3000);
		Thread.sleep(sleep);
		return sleep;
	}
}

Der Async-Tag unterstützt weiterhin die Attribute styleClass, style und das Facet "preview", mit dem ein Vorab-Content angezeigt werden kann:

    <e:async>
        <f:facet name="preview">
            <h:outputText value="Loading content..."/>
        </f:facet>
    </e:async>

For-Tag#

Der Tag <e:for> ist ein Schleifen-Tag mit erweiterten Möglichkeiten. Er kombiniert die Naming-Container Eigenschaft von <ui:repeat> mit der Flexibilität von <c:forEach> und ist darüber hinaus noch angenehm kurz.

Folgender XHTML-Code erzeugt eine Reihe von Knöpfen:

	<h:form id="form">
		<e:for id="for" value="#{table.list}" var="element">
			<h:commandButton id="button" value="#{element.text}" action="#{element.action}">
				<f:ajax/>
			</h:commandButton>
		</e:for>
	</h:form>

Das Attribut value kann dabei durch eine Collection, Map, Array, einzelnes Element oder einen Null-Wert versorgt werden.

Mit dem Attribut test können Elemente aus der Schleife gefiltert werden, entsprechend dem Syntax von <c:if test="...">.

Vertical Text Tag#

Vertikaler Text, also um 90° gedrehter Text kann in einigen Fällen sehr hilfreich sein. Zum Beispiel um Platz zu sparen beim Beschriften von Header in Tabellen. Die Browser rendern Anweisungen für vertikale CSS-Anweisungen nur sehr uneinheitlich, wenn überhaupt.

Daher bietet JSF Ext einen Tag für das Rendern von vertikalen Text:

<e:vertical-text value="Hello World!" fontSize="16"/>

AttributBedeutung
fontSizeDie Größe des Schriftsatz in Point (pt)
widthBreite der gerenderten Grafik in Pixel
heightHöhe der gerenderten Grafik in Pixel
fontNameName des Fonts
colorFarbe der Schrift

Facet Tag#

Dem Standard Tag <f:facet> fehlt leider das Attribut rendered. Man kann auch kein <c:if> herum wrappen. Bei Tabellen löst beispielsweise bereits der leere Tag "header" das Rendern des Headers aus. Der Tag <e:facet> implementiert dieses Attribut.

Embed Tag#

Einige Zeit lang war Portlet das Stichwort für verteilte Web-Anwendungen. Leider hat sich die Implementierung als wenig praktikabel erwiesen, daher sind viele Firmen wieder davon abgerückt.

Eine einfachere Lösung für verteilte Anwendungen ist das Einbetten von Inhalten mit dem Tag <e:embed src="...">. Die Inhalte können vom gleichen Server, der gleichen Anwendung oder einer ganz anderen Quelle kommen. Da mit IFrames gearbeitet wird, ist nicht einmal JSF erforderlich oder die gleichen Komponentenbibliotheken.

Render-Tag#

Der Tag <e:render> ist ein nützlicher Tag für dynamische Web-Seiten, in denen Teile automatisch refreshed werden. Bisher gibt man im Ajax-Tag eine Liste von Elementen an, die neu gerendered werden sollen.

Die alte Implementierung über Render-Attribute führt zu folgenden Verbesserungswünschen:

  • Dynamik: Zum Zeitpunkt des Ausführens der Render-Anweisung ist unklar, welche Bereiche überhaupt neu gerendered werden brauchen. Die Zielbereiche können über das Render-Attribut, Scopes und andere Mechanismen dynamisch ein- und ausgeblendet werden. Man möchte die Veränderung einer Struktur melden können, unabhängig von den zur Laufzeit tatsächlich abhängigen Elementen.
  • Weiterentwicklung: Nach Schreiben der Render-Anweisung wird die Anwendung weiterentwickelt. Zielbereiche zum Rendern können hinzukommen oder wegfallen, dies führt zu permanenter Pflege einer wachsenden Zahl von Render-Attributen in der Anwendung. Gewünscht ist, dass fertiger Code nicht permanent wieder angefasst werden braucht.
  • Modularisierung: Bei Modulen, die durch JAR-Dateien ins Projekt kommen, kann auf die XHTML-Seiten kein Einfluss genommen werden. Dort soll ebenfalls ein Rendering möglich sein.

Die Lösung sind Events und der Render-Tag. Typischer Weise werden Events durch den Code ausgelöst, wie etwa beim Login:

Event.instance().raise(EVENT_LOGIN, authentication.getPrincipal());

Dadurch werden Bereiche neu gerendered:

<h:form id="process-form" enctype="multipart/form-data" styleClass="ui-widget" style="margin-left: 10px;">
	<e:render event="intersult.subflow.Authenticator.login"/>
	<e:render event="intersult.subflow.Process.change"/>
	<e:render event="intersult.subflow.Process.select"/>
	
	<e:div rendered="#{!empty processDetails.process}">
		...
	</e:div>
</form>

Hinweis: Es können nur Bereiche neu gerendered werden, die bereits gerendered wurden. Dadurch können Render-Tags dadurch problemlos in Scopes und anderen dynamischen Berechen verwendet werden. Es ist völlig verträglich, wenn ein Abschnitt mit einem Render-Tag selbst nicht gerendered wurde, dieser wird dann einfach ignoriert.

Tipp: Soll ein Bereich durch ein Render-Tag ein- und ausgeblendet werden, platziert man den Render-Tag in die übergeordnete Component. So wird die Region auf jeden Fall gerendered, unabhängig vom Render-Zustand (Render-Attribut, <c:if> etc.) der innen liegenden Sektion.

Action Support#

Action Tag#

Manchmal kann es erforderlich sein, eine Method-Expression als Component zu übergeben oder mehrere Method-Expressions aufzurufen. Dafür kann der Tag <e:action> verwendet werden:
<h:commandButton value="Invoke Actions">
    <f:ajax/>
    <e:action listener="#{bean.action1}"/>
    <e:action listener="#{bean.action2}"/>
    <e:action listener="#{bean.action3}"/>
</h:commandButton>

Action Context Tag#

In JSF ist es vorgesehen, dass beim Ausführen einer Action-Methode die ausführende Komponente auf dem Component-Stack gepushed wird. Das bedeutet unter anderem, dass die Komponente mit UIComponent.getCurrentComponent(context) abgefragt werden kann. Dies ist wichtig, wenn man mit <f:attribute> gesetzte Werte abfragen möchte.

Der Workaround besteht im Wrappen des Tags <e:actionContext> um die eigentliche Action-Source:

<p:treeTable id="table" value="#{cc.attrs.value}" var="node">
    <p:column>
        <e:actionContext>
            <p:autoComplete ...>
                <f:attribute name="param" value="#{node.someValue}"/>
            </p:autoComplete>
        </e:actionContext>
    </p:column>
</p:treeTable>

Erklärung: Bei der Primefaces Tree-Table wurde offenbar vergessen der Component-Push

Composite Tags#

JSF Ext enthält auch bereits einige Composite Tags, diese sind unter dem Namespace xmlns:ext="http://java.sun.com/jsf/composite/ext" verwendbar.

Busy Pointer#

Die Integration von AJAX in die Anwendung findet vom Browser zunächst transparent statt. Der Benutzer hat keinen Hinweis, dass gerade eine Server-Aktion erfolgt. Der Vorteil ist, dass der Benutzer mit dem Browser normal weiter arbeiten kann. In vielen Fällen ist dies auch möglich, da Operationen unabhängig voneinander ausgeführt werden können. Der Nachteil ist, dass der Benutzer keinen Hinweis erhält, dass die beabsichtigte Operation angelaufen ist.

In den Anwendungen werden daher unterschiedlichste Elemente in die Seite eingebaut, von rotierenden, blinkenden durch durchlaufenden Bildern bis zu Seiten abdunkelnden und sperrenden Elementen. Das übliche Verfahren von Anwendungen die Verarbeitung einer Operation zu zeigen, ist den Mauszeiger als Busy Pointer darzustellen.

Also wieso nicht auch im Browser bei JSF-Anwendungen den Busy Pointer aktivieren. Der Benutzer wird nicht abgelenkt durch zappelnde Bilder, es wird nichts gesperrt, da die Seite ja weiterhin funktional ist:

<ext:busyPointer/>

Optional kann das Attribut cursor angeben werde, das per default auf "wait" gesetzt ist.

Mouse Visibility#

Auf Seiten die sehr viele Elemente enthalten kann es die Übersicht verbessern, wenn Elemente erst bei Mouse-Over angezeigt werden. Durch reine CSS-Styles ist das meist nicht zu realisieren, weil das Mouse-Over-Element ein Parent-Element des anzuzeigenden Elements ist. Lösungen mit A-Tags, die man häufig findet, führen zu Nebenwirkungen wie Anchor-Links und ungewollten GET-Requests.

Eine Lösung ist der Tag <ext:mouse-visibility>:

<e:div>
	<h:outputText id="project" value="#{processDetails.process.project.name}"/>
	<ext:mouse-visibility for=":process-form:image"/>
	<h:graphicImage id="image" name="edit.gif" library="images/bitcons">
		<e:ajax event="click">
			<e:load scopeId=":projectEdit">
				<f:param name="project" value="#{project}"/>
			</e:load>
		</e:ajax>
	</h:graphicImage>
</e:div>

Darüber hinaus gibt es noch die Tags <ext:mouse-display> mit welcher der Display-Style eines Elements gesteuert werden kann. Besonders schick ist auch der <ext:mouse-fade>, mit der das Element ein- und ausgefadet wird.

Onscroll Tag#

In den letzten Jahren ist das automatische Nachladen von Content modern geworden, wenn die Web-Seite am unteren Ende angelangt ist. Dies lässt sich leicht mit dem Tag <ext:onscroll> realisieren:
<ext:onscroll>
	<e:ajax action="#{dashboard.more}" render=":dashboard-form:transaction-list:list"/>
</ext:onscroll>

Erklärung: Der Tag führt das Behavior <e:ajax/> aus, sobald ans untere Ende der Page gescrollt wird.

JSF-Resources können mit <h:outputStylesheet> und <h:outputScript> ausgegeben werden, mit <h:graphicImage> als Image angezeigt. Einige weitere Tags unterstützen die Attribute "name" und "library". Darüber hinaus gibt es die Möglichkeit, Resource-URLs mit
#{resource['<library>:<name>']}

zu erzeugen oder ganz zu Fuß mit

#{request.contextPath}/faces/javax.faces.resource/<name>?ln=<library>

Möchte man einen Link erzeugen vom Schema <a href="...">, bietet JSF Ext eine schönere Lösung:

<e:resourceLink name="<name>" library="<library">
    <h:outputText value="Some link"/>
</e:resourceLink>

Hinweis: Wie der normale <h:outputLink> unterstützt <h:resourceLink> ebenfalls Parameter vom Typ <f:param>. Des Weiteren ergänzt sich der Link gut mit Resource Provider

Faces Messages#

Das Rendern von Faces Messages braucht üblicher Weise eine explizite Rendering Message. Wird es vergessen, kann der Benutzer nicht erkennen, welches Feld falsch eingegeben wurde.

Einfache Message#

JSF Ext stellt daher selbst rendernde Faces-Messages zur Verfügung als Tag <ext:message>.

Severity Tag#

Oft sind Seiten komplex aufgebaut und Steuerelemente versteckt. Dafür gibt es den Tag <ext:severity prefix="...">. Dieser Zeigt ein Severity-Icon an für alle Messages mit einem bestimmten Prefix, das heißt für einem Nameing-Container untergeordnete Components.

Der Severity-Tag ist eine Composite Component mit folgender Implementierung:

	<h:panelGroup id="messages" style="#{cc.attrs.style}" styleClass="#{cc.attrs.styleClass}">
		<e:render id="render" for=":#{cc.attrs.prefix}:*"/>
		<h:graphicImage name="info.png" library="images/message"
			rendered="#{e:severity(cc.attrs.prefix) == 'INFO'}"/>
		<h:graphicImage name="warn.png" library="images/message"
			rendered="#{e:severity(cc.attrs.prefix) == 'WARN'}"/>
		<h:graphicImage name="error.png" library="images/message"
			rendered="#{e:severity(cc.attrs.prefix) == 'ERROR'}"/>
	</h:panelGroup>

Captcha Component#

In JSF Ext ist eine Captcha Component enthalten. Diese bringt ihre eigene Graphic-Resource mit, es werden also keine externen URLs angesprochen. Das Captcha ist also voll kompatibel zu HTTPS und AJAX. Kein dritter Provider kann ein Tracking der Site vornehmen.

Eigenschaften#

Bei Captchas gibt es einige Unterschiede bezüglich des Lifecycle und der Validierung:
  • Default Lifecycle: Das Captcha wird in der Session abgelegt. Grundsätzlich führt ein Reload der Page zur Anzeige desselben Captchas.
  • Neues Captcha: Durch den Refresh-Knopf wird ein neues Captcha angezeigt. Die Klasse "Captcha" bzw. die Bean "captcha" kann das Captcha mit der Methode "reset" zurückgesetzt werden, sodass beim Rerendering ein neues Captcha angezeigt wird.
  • AJAX: Es spielt keine Rolle, ob das Captcha direkt im HTTP-Request geladen wird, oder ob es durch einen AJAX-Request nachgeladen wird. Im Gegensatz zu komplizierten IFrame und Javascript-Lösungen funktioniert <e:captcha> in den meisten Umgebungen.
  • Wiederholte Validierung: Die Eingabe kann ein- oder mehrfach positiv validiert werden. Das Captcha wird dabei nicht verändert, wenn es einmal erfolgreich validiert wurde. Der Benutzer kann damit andere Validierungsfehler beheben, ohne das Captcha ständig neu eingeben zu müssen.
  • Negative Validierung: Bei negativer Validierung, also Validation failed, wird ein neues Captcha generiert. Damit wird vermieden, dass der Anwender denselben Lesefehler wiederholt.

Beispiel#

Das Captcha kann als Component in die Seite eingebaut werden:
	<h:form id="form">
		<e:captcha id="captcha"/>
		<ext:message for=":form:captcha"/>
		
		...
	</h:form>

Erklärung: Die Captcha Component rendert ein PNG-Image mit dem entsprechenden Text, sowie ein Eingabefeld, in dem der Text durch den Benutzer eingegeben wird. Die Component hat eine positive Validierung, wenn das Captcha korrekt ist, andernfalls schlägt die Validierung fehl und eine Faces Message wird eingefügt. Das Fehlschlagen der Validierung verhindert das Ausführen von Action Methoden, die an Command Buttons und ähnlichem hängen.

Um die Captcha Component zu verwenden, ist folgendes Maven Artifact in der pom.xml einzubinden:

	<dependency>
		<groupId>com.octo.captcha</groupId>
		<artifactId>jcaptcha</artifactId>
		<version>1.0</version>
		<exclusions>
			<exclusion>
				<artifactId>servlet-api</artifactId>
				<groupId>javax.servlet</groupId>
			</exclusion>
		</exclusions>
		<scope>provided</scope>
	</dependency>

Hinweis: Das Artifact hat die servlet-api im Scope "compile" eingebunden. Um das Deployen der servlet-api.jar zu vermeiden, ist die Exclusion erforderlich. Andernfalls kann es zu Problemen beim Deployment kommen.

Behavior Tags#

Characters Left#

Bei Steuerelementen von Text-Areas oder Text-Input kann es von Vorteil sein, die Anzahl der möglichen Zeichen sehen zu können. Dazu fügt der Tag <e:charactersLeftBehavior> einen Zähler ein:
<h:inputTextarea id="comment" value="#{processDetails.comment}">
	<e:charactersLeftBehavior event="keyup"/>
</h:inputTextarea>

Erklärung: Die Anzahl möglicher Zeichen werden den Javax-Validation-Tags @Size und @Length oder dem LengthValidator entnommen. Die verbleibenden Zeichen werden dann im Text-Feld eingeblendet, die Eingabe wird auf die maximale Zeichenzahl begrenzt.