This page (revision-184) was last changed on 21-Apr-2017 08:27 by Dieter Käppel

This page was created on 09-Aug-2012 13:29 by Dieter Käppel

Only authorized users are allowed to rename pages.

Only authorized users are allowed to delete pages.

Page revision history

Version Date Modified Size Author Changes ... Change note
184 21-Apr-2017 08:27 36 KB Dieter Käppel to previous
183 21-Apr-2017 08:27 36 KB Dieter Käppel to previous | to last
182 15-Jan-2016 10:18 36 KB Dieter Käppel to previous | to last
181 15-Jan-2016 10:16 36 KB Dieter Käppel to previous | to last

Page References

Incoming links Outgoing links

Version management

Difference between version and

At line 1 changed 2 lines
[{Image src='jsf-ext.jpg' width=130 style='display: block; float: right; margin-left: 5px; margin-top: -5px;'}]
[JSF Ext] oder [JSF Extensions|JSF Ext] ist nun die dritte Auflage unserer beliebten Erweiterungen für JSF. Nach den Versionen [Intersult Taglib] für JSF 1.2, [JSF Desktop] für JSF 2.0 ist nun [JSF Ext] für JSF 2.0 bis 2.2 verfügbar. [JSF Ext] ist eine Erweiterung von JSF 2 (2.0, 2.1, 2.2) um einige grundlegende Features, die man für saubere, funktionale AJAX-Applikationen braucht.
[JSF Ext] oder [JSF Extensions|JSF Ext] (vorher JSF Desktop) ist eine Erweiterung von JSF 2 (2.0, 2.1) um einige grundlegende Features, die man für saubere, funktionale AJAX-Applikationen braucht.
At line 4 changed one line
[JSF Ext] wurde weiter vereinfacht, sodass eine hohe Geschwindigkeit der Web-Seiten erreicht werden kann. [JSF Ext] wurde optimiert, sodass höchst mögliche Kompatibilität mit anderen Produkten sichergestellt ist. Sollten Sie Fragen, Bugs oder Feature-Requests haben, so nutzen sie bitte die [Issue List|https://www.sub-flow.com/subflow/project/SUBFLOW].
Einige der Features:
At line 6 changed one line
Übersicht der Features:
* Vereinfachte Fehlerbehandlung
* Dynamisches Laden von AJAX-Facelets
* Facelet-Scopes mit komfortabler Load-/Unload-Funktion
* Einfach nutzbare Event-Erzeugung und -Empfang
At line 8 removed 18 lines
* __Dynamik:__ Echtes dynamisches Laden von Teilen großer Views
* __Scopes:__ Component-Scopes mit komfortabler Load-/Unload-Funktion
* __Messages:__ Vereinfachte Fehlerbehandlung
* __Events:__ Einfach nutzbare Events und automatisches Rendering
* __Push:__ AJAX-Push unabhängig von verwendeten Framework
* __Kompatibilität:__ Arbeitet zusammen mit Primefaces, Richfaces, Spring etc.
* __Fertigstellung/Fixes:__ Multipart-Requests (Submit/AJAX), View State Fix, Compatibility, AJAX Insert und Delete etc.
* __Persistence:__ [JSF Ext] läuft auch ohne Hibernate, diese Funktionalität befindet sich in [JPA Support].
Folgende Inhalte befinden sich auf eigenen Wiki-Seiten:
* [JSF Ext Tags]
* [JSF Ext Scopes]
* [JSF Ext Events]
* [JSF Ext Push]
* [JSF Ext Functions]
* [Faces Filter]
At line 22 added 5 lines
!!!Kompatibilität
Demo unserer [Desktop-Plattform|Intersult Plattform] auf der Basis von [JSF] v2, die auch auf [GAE|Google Application Engine] lauffähig ist:
Damit setzen Sie komplexe, interaktive [Applications|Application] im Handumdrehen auf.
At line 39 changed one line
[JSF Ext] kann von [Central|http://search.maven.org/] bezogen werden:
Die Anwendung befindet sich im [Intersult Maven Repository]:
At line 41 changed one line
In der pom.xml für den Maven-Build wird angegeben:
In der pom.xml wird angegeben:
At line 44 changed 12 lines
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>com.intersult</groupId>
<artifactId>jsf-ext</artifactId>
<version>2.2.0.1</version>
</dependency>
...
</dependencies>
</project>
<dependency>
<groupId>com.intersult</groupId>
<artifactId>jsf-ext</artifactId>
<version>2.1.11-SNAPSHOT</version>
</dependency>
<repository>
<id>intersult-repo</id>
<name>Intersult Repository</name>
<url>http://intersult.com/public/maven</url>
</repository>
At line 65 removed 5 lines
!!Kompatibilität
Demo unserer [Desktop-Plattform|Intersult Plattform] auf der Basis von [JSF] v2, die auch auf [GAE|Google Application Engine] lauffähig ist:
Damit setzen Sie komplexe, interaktive [Applications|Application] im Handumdrehen auf.
At line 85 changed 2 lines
!!AJAX-Queue
Üblicher Weise werden AJAX-Aufrufe serialisiert. Generell macht dies Sinn, da es sonst zu Problemen mit dem View-State kommt. Aktiviert wird das Parallelisieren von AJAX-Aufrufen durch das Setzen der JavaScript-Variable "jsf.ajaxQueue":
!!!Tags
Das [JSF Ext] enthält eine Reine von Tags.
At line 88 changed 5 lines
{{{
<script type="text/javascript">
jsf.ajaxQueue = 2;
</script>
}}}
!!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/...
At line 94 changed one line
__Warnung:__ Aus gutem Grund werden die AJAX-Requests üblicher Weise serialisiert. Beim Verwenden paralleler AJAX-Requests besteht die Gefahr, einen veralteten View-State an den Server zu senden. Parallele Requests werden nur korrekt arbeiten, wenn ausgeschlossen wird, dass ein Formular mehrere Abfragen gleichzeitig abschickt.
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.
At line 96 changed 2 lines
!!!Tag-Support
[JSF Ext] bietet eine Reihe von Features, die den Bau von eigenen Tags unterstützen.
__Lösung:__ Man iteriert durch die Children mittels <c:forEach> und fügt diese an einer beliebigen Stelle mittel <e:insert> ein.
At line 99 removed 11 lines
Composite Tags sind ein mächtiges und zugleich einfach zu handhabendes Instrument zum Erstellen eigener Taglibs. Durch einige zusätzliche Instrumente werden Composite Tags noch vielseitiger. Damit kann vielfach auf die Entwicklung von nativen Tags verzichtet werden, inklusive aufwändiger Compile, Redeploy und Restart-Zyklen. Durch die erweiterten Möglichkeiten steigt gleichzeitig der Anreiz zu Composite Tags.
!!Composite Behaviors
Beim Bau von Composite-Tags möchte man in einigen Fällen auf den darüberliegenden Tag zugreifen.
__Hintergrund:__ Die EL-Expression #{component.parent.parent.parent} (dazwischen zwei UINamingContainer und UIPanel die Facelets einfügt) würde normaler Weise den Parent-Tag liefern, evaluiert jedoch zu null. Zum einen ist zu dieser Zeit die Tag-Hierarchie noch nicht aufgebaut, zum anderen wird der UINamingContainer aus der Facelet-Hierarchie herausgenommen, da der Tag durch einen speziellen Mechanismus gerendert wird.
__Lösung:__
Zugriff über jsf, also #{jsf.tag.composite.parent.component}. Folgendes Beispiel zeigt eine Composite-Component für ein Mouse-Behavior:
At line 113 removed 2 lines
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
At line 82 added one line
xmlns:h="http://java.sun.com/jsf/html"
At line 84 added one line
xmlns:ui="http://java.sun.com/jsf/facelets"
At line 90 added one line
<cc:attribute name="align" default="center"/>
At line 124 changed 2 lines
<e:behavior id="mouseover" event="mouseover" for="#{jsf.tag.composite.parent.component}" script="focus();"/>
<e:behavior id="mouseout" event="mouseout" for="#{jsf.tag.composite.parent.component}" script="blur();"/>
<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>
At line 130 changed one line
Dieser Tag ist bereits Bestandteil des JSF-Ext unter <ext:mouse-focus>. Er kann zusammen mit jedem ClientBehaviorHolder, wie dem <e:div> angewendet werden.
!!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.
At line 132 changed 2 lines
!!Forms und AJAX
Wird eine Komponente durch AJAX neu gerendered, die eine FORM enthält, geht der View-State der FORM verloren. In einigen Fällen kann dies verhindert werden, in dem die Form explizit im Render-Attribut eines AJAX-Tags angegeben wird. Im Zusammenhang mit Include-Anweisungen, wie auch den Scopes ist dies nur bedingt möglich.
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.
At line 135 changed one line
[JSF Ext] löst dieses Problem durch zusätzlichen Java-Script Code. Durch das Einbinden der JAR-Bibliothek ist das Problem transparent gelöst. Der enthaltene Java-Script Code wird immer dann eingebunden, wenn auch der JSF-AJAX-Code eingebunden wird. Er fügt den ViewState in enthaltenenen FORM-Tags ein, sodass keine zusätzlichen Render-Anweisungen benötigt werden.
Lösung bringt der Behavior-Tag von [JSF Ext]. Typisch ist zum Beispiel die Verwendung zusammen mit dem AJAX-Tag:
At line 137 removed 5 lines
!!Der ExtResponseWriter
JSF-Behaviors bestehen aus einer Klasse die von Behavior oder ClientBehavior abgeleitet sind. Im Wesentlichen ist eine Methode getScript enthalten, mit der ein Fragment des entsprechenden event-Attribut des entsprechenden HTML-Tags gerendert wird, also zum Beispiel onclick="...". Bei komplexeren Behaviors braucht man zusätzlichen HTML-Code, der kann an dieser Stelle nicht geschrieben werden. Ursache ist dass der HTML-Writer sich gerade innerhalb des geöffneten Tags befindet und startElement ungültiges HTML erzeugen würde.
Der ExtResponseWriter von [JSF Ext] stellt eine statische Methode getEndElementWriter(ResponseWriter responseWriter) bzw. getEndElementWriter() zur Verfügung. Damit bekommt man einen ResponseWriter, mit dem geschrieben werden kann. Der HTML-Code wir dann effektiv vor dem Schließen des End-Tags ausgegeben:
At line 143 changed 5 lines
ResponseWriter writer = ExtResponseWriter.getEndElementWriter();
writer.startElement("div", component);
writer.writeAttribute("id", component.getClientId(behaviorContext.getFacesContext()), null);
writer.writeAttribute("style", "display: none;", null);
writer.endElement("div");
<h:commandButton value="X">
<f:ajax/>
<e:unload/>
<e:behavior script="#{rich:component('popup-panel')}.hide(event);"/>
</h:commandButton>
At line 150 changed 2 lines
!!Components referenzieren
In JSF gibt es drei Arten von Referenz-Ausdrücken in render und execute Ausdrücken, relative, absolute und meta:
Die Behavior-Snippets werden in der entsprechenden Reihenfolge produziert:
At line 153 removed 8 lines
* __Absolute:__ Beispielsweise :form:panel:input-text
* __Relative:__ panel:input-Text
* __Meta:__ @this, @component, @form, @all
Meist arbeitet man soweit es geht mit relativen Ausdrucken und wechselt notfalls zu absoluten. Um die allgemeine Verwendbarkeit von absoluten Ausdrücken in Composite Components zu gewährleisten, beginnt man diese etwa mit :#{cc.clientId}:input-Text. Das funktioniert leider nicht immer, insbesondere wenn Tabellen, For-Tags oder andere dynamische Elemente darüber liegen.
Dazu führt [JSF Ext] die Syntax ".." ein, die man bereits von relativen Pfadangaben im Dateisystem kennt. Damit ist es nun möglich innerhalb einer Composite Component einen oder einige Components nach oben zu gehen und von dort ab etwas zu selektieren. Also die Kombination von relativen Angaben und Erreichbarkeit der Components:
At line 162 changed one line
<e:ajax render="..:#{cc.id}:panel"/>
jsf.util.chain(this,event,'mojarra.ab(this,event,\'action\',0,0)','RichFaces.$(\'popup:form:popup-panel\').hide(event);');return false
At line 165 changed one line
__Hinweis:__ Jede Angabe von ".." geht eine Naming-Container nach oben. Components die kein Naming Container sind, werden dabei übersprungen, ebenso wie beim ursprünglichen Absteigen im Component Tree mit x:y:z.
!!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.
At line 167 changed 2 lines
!!!Internationalisierung
[JSF Ext] enthält einige Erweiterungen, die den Umgang mit Sprach- und Länderunterstützung erleichtern. Es sind SelectItems für Sprachen, Länder und Währungen vorhanden, damit können unter anderem Drop-Down-Menüs befüllt werden:
!Sources
Manchmal ist es sinnvoll andere Sources abzuschicken, dafür stellt [JSF Ext] den Tag <e:ajax> zur Verfügung:
At line 171 changed 9 lines
<h:selectOneMenu id="country" value="#{bean.country}">
<f:selectItems value="#{locales.countrySelectItems}"/>
</h:selectOneMenu>
<h:selectOneMenu id="language" value="#{bean.language}">
<f:selectItems value="#{locales.languageSelectItems}"/>
</h:selectOneMenu>
<h:selectOneMenu id="currency" value="#{bean.currency}">
<f:selectItems value="#{locales.currencySelectItems}"/>
</h:selectOneMenu>
<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>
At line 182 changed one line
Zusätzlich befindet sich noch ein Getter für localeSelectItems. Dies liefert Liste vollwertiger Locale-Elemente aus Locale.getAvailableLocales() in einer auf der Oberfläche darstellbaren Form.
Der Tag <e:ajax> ersetzt in diesem Fall den Tag <f:ajax>, er braucht nicht zusätzlich angegeben zu werden.
At line 184 changed one line
Des Weiteren befindet sich ein Feld für eine User-Locale in der Session-Scoped Bean. Dadurch kann ein einfaches Auswählen der Locale erreicht werden:
!Action Source
Der Tag kann auch Actions abschicken, wenn er an einfachen ClientBehaviorHolder gehängt wird, weil er selbst eine ActionSource darstellt:
At line 187 changed 3 lines
<h:selectOneMenu id="language" value="#{locales.locale}" converter="intersult.String">
<f:selectItems value="#{locales.localeSelectItems}"/>
</h:selectOneMenu>
<h:panelGrid>
<f:ajax event="dblclick" action="#{bean.action}"/>
</h:panelGrid>
At line 192 changed one line
Der Converter "intersult.String" konvertiert ein Locale-Element in einen Sprach-String und zurück, damit das Locale-Objekt mit dem selectOneMenu verwendet werden kann.
!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:
At line 194 removed 28 lines
!!Properties Resources
In [JSF] können Properties-Dateien definiert werden, die entsprechend der Endung im Namen für die Internationalisierung verwendet werden können. Um auch aus dem Java-Code darauf zugreifen zu können, gibt es die Klasse com.intersult.jsf.messages.Resource:
||Methode||Beschreibung
|getString|Den Resource-String zurückgeben. Falls nicht vorhanden, wird ein Dummy zurückgegeben.
|getStringNull|Den Resource-String zurückgeben. Falls nicht vorhanden, wird null zurückgegeben.
|getStringVariant|Eine Variante eines Resource-Strings zurückgeben.
|getFormatVariant|Eine formatierte Variante eines Resource-Key zurückgeben.
|getFormat|Einen formatierten Resource-String zurückgeben.
|addMessage|Eine globale Faces-Message aus einem Resource-Key hinzufügen.
|addMessageToComponent|Eine Faces-Message aus einem Resource-Key zu einer Komponente hinzufügen.
|getMessage|Eine Faces-Message aus einem Ressource-Key erzeugen
|enumFormat|Einen formatierten Resource-String für ein Enum zurückgeben.
|enumFormatVariant|Eine Variante eines formatierten Resource-String für ein Enum zurückgeben.
|enumString|Einen Resource-String für ein Enum zurückgeben.
|enumList|Eine Liste mit Enums zurückgeben.
|enumStringList|Eine Liste mit Enum-Strings zurückgeben.
|enumStringListVariant|Eine Liste mit Varianten von Enum-Stringe zurückgeben.
|enumSelectItems|SelectItems für das Befüllen von JSF-Elementen (Dropdowns etc.) zurückgeben
|enumSelectItemsType|SelectItems für einen Enum-Type zurückgeben.
|enumSelectItemsTypeVariant|SelectItems für Varianten eines Enum-Types zurückgeben.
|enumSelectItem|Ein einzelnes SelectItem erzeugen.
|abbreviate|Einen String für die Oberfläche abkürzen.
|replaceHtml|HTML-Sonderzeichen ersetzen.
!!!Spring Framework
[JSF Ext] arbeitet mit dem Spring Framework zusammen. Für die Spring-JSF-Integration ist es sinnvoll zunächst den EL-Resolver von Spring zu registrieren:
At line 223 changed 5 lines
<faces-config>
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
</faces-config>
<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>
At line 230 changed 2 lines
!!Scopes
Wenn man nicht nur JSF Managed Beans im Custom Scope von [JSF Ext] haben möchte, kann dieser auch in der applicationContext.xml registriert 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.
At line 172 added 2 lines
Der Tag <e:div> ist ein vollwertiger ClientBehaviorHolder:
At line 234 changed one line
<import resource="classpath:/META-INF/extContext.xml"/>
<e:div id="mouse-active" tabindex="0" onkeypress="alert((event || window.event).keyCode);">
<ext:mouse-focus id="mouse-focus"/>
<h1>TEST</h1>
</e:div>
At line 237 changed one line
__Hinweis:__ [JSF Ext] registriert zusätzlich den ViewScope von JSF.
Damit wird das Erstellen von einfachen Komponenten mit wenig HTML-Code und flachen Komponenten-Bäumen deutlich vereinfacht.
At line 239 changed 2 lines
!Beispiel
Zum Beispiel wäre dann folgende Konstruktion möglich:
!!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:
At line 242 removed 2 lines
Beim Laden des Scopes:
At line 245 changed 3 lines
<e:load scopeId="rule-remove">
<f:param name="rule" value="#{rule}"/>
</e:load>
<h:inputText id="name" value="#{bean.name}">
<e:attribute name="placeholder" value="Name eingeben..."/>
</h:inputText>
At line 250 changed one line
Innerhalb des Scopes:
!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:
At line 253 changed 4 lines
<h:commandButton value="#{messages['remove']}" action="#{ruleEditController.remove}">
<f:ajax/>
<e:unload/>
</h:commandButton>
<h:panelGroup>
<e:attribute name="oncontextmenu">
<f:ajax listener="#{bean.render}" render=":some-menu"/>
</e:attribute>
</h:panelGroup>
At line 259 changed one line
Der Controller dazu:
!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:
At line 262 changed 11 lines
@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class RuleEditController {
@ScopeValue
private Rule rule;
@Transactional
public void remove() {
...
}
}
<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>
At line 275 changed one line
__Erklärung:__ Damit ist es möglich, eine Entity- oder andere Modell-Bean innerhalb eines Scopes zu bearbeiten, dessen Lebenszyklus an ein AJAX-Popup gekoppelt ist. Es ist keine weitere Java-Bean erforderlich, die in einem Scope abgelegt werden muss. Alle parameterisierten Beans werden beim Beenden (Unload) des Scopes freigegeben.
!!Reference-Tag
__Vorwort:__ Generell ist der Tag <e:scope> vorzuziehen, da er ausgereifter ist und mehr Funktionalität bietet. In einzelnen Fällen kann dennoch der Tag <e:reference> von vorteilhaft sein.
At line 277 changed one line
__Hinweis:__ Der Controller ist mit der Annotation @Scope(WebApplicationContext.SCOPE_REQUEST), damit die Injection im richtigen Scope erfolgt. Der Scope kann sich mit jedem Request ändern.
Der Reference-Tag schafft Zugriff auf den ELContext einer andere Stelle im Komponenten-Baum. Über eine festgelegte Variable kann auf Elemente aus einem beliebigen Kontext zugegriffen werden. Einschließlich iterierte Kontexte, wie sie von <ui:repeat> oder Tabellen erzeugt werden.
At line 279 changed one line
__Hinweis:__ Statt die ineffiziente Value-Injection über EL-Expression stellt die [JSF Spring Integration] eine direkte Injection von Scope-Werten durch die Annotation @ScopeValue zur Verfügung.
In JSF werden oft Components verwendet, die durch Iteration mit <ui:repeat> oder auf andere Art vervielfältigt werden, auch Tables, Trees und ähnliche Komponenten geben wiederholten HTML-Code aus.
At line 281 changed 2 lines
!Ablegen einer Spring-Bean im Scope
Spring Beans können auch im Custom-Scope abgelegt werden:
Gerade bei vervielfältigtem Code möchte man diesen schlank bauen, da dies für die Effizienz der Web-Seite ausschlaggebend ist. Kontext-Menüs, Popups und andere Elemente, die nur einmal für ein Element geöffnet werden brauchen, sollten also aus der Iteration herausgenommen werden. Doch früher oder später muss auf das Element wieder zugegriffen werden.
At line 225 added 2 lines
In einfachen, wenig zeitkritischen Fällen behilft man sich mit einem AXAJ-Submit. Zum Beispiel:
At line 285 changed 5 lines
@Component
@Scope(Scopes.CUSTOM_SCOPE)
public class Popup {
...
}
<h:form id="form">
<ui:repeat id="repeat" value="#{fn:split('1,2,3,4,5', ',')}" var="index">
<h:commandButton id="button" value="Button #{index}">
<f:setPropertyActionListener target="#{bean.selected}" value="#{item}">
<e:ajax onsuccess="showContextMenu();"/>
</h:commandButton>
</c:forEach>
</h:form>
At line 292 changed one line
Oder im View-Scope:
__Anmerkung:__ In JSF 2.1.11 ist für den Tag <ui:repeat> kein Attribut "id" in der Taglib eingetragen. Da es sich um eine UIComponent handelt, kann die Id trotzdem angegeben werden und wird auch korrekt verarbeitet. Damit erreicht man, dass die Child Components der Iteration definierte Namen bekommen.
At line 240 added 2 lines
Allerdings wird dazwischen ein AJAX-Aufruf abgeschickt. Wenn in einem Popup oder Kontextmenu das Element betreffende Dinge neu gerendered werden sollen, sicher der richtige Weg. Bei einfacheren Komponenten oft hinderlich und vom Benutzer als zu "hackig" empfunden. Da kann der Reference-Tag Abhilfe schaffen:
At line 295 changed 5 lines
@Component
@Scope(Scopes.VIEW_SCOPE)
public class Popup {
...
}
<h:form id="form">
<c:forEach begin="1" end="5" var="index">
<h:commandButton id="button-#{index}" value="Button #{index}">
<e:setReference target=":form:ref"/>
<e:behavior script="showContextMenu();" disableDefault="true"/>
</h:commandButton>
</c:forEach>
<e:reference id="ref" var="ref">
<h:commandButton value="Show" action="#{test.actionText(ref.index)}">
<f:ajax/>
</h:commandButton>
</e:reference>
</h:form>
At line 302 changed 2 lines
!!Events
Um die Annotation @Listener auch in Spring-Beans verwenden zu können, kann man den EventBeanProcessor in Spring registrieren:
__Erklärung:__ Der Tag <e:setReference> ist ein ClientBehavior, also JavaScript für den Tag <e:reference>. Beim Klicken eines der iterierten Command-Buttons wird die Referenz des Tags <e:reference> gesetzt, ohne einen Request zum Server abzuschicken. Beim Klicken des unteren Command-Buttons "Show" wird mit "ref.index" auf den ELContext des referenzierten Buttons zugegriffen, also die Variable "index" die zum Iterieren verwendet wurde.
At line 260 added 3 lines
!!New-Tag
Der Tag <e:new> kann an jeder Stelle verwendet werden, an der auch <f:param> verwendet wird:
At line 306 changed one line
<bean class="com.intersult.jsf.spring.EventBeanProcessor"/>
<e:load scopeId="test-scope">
<e:new name="bean" type="com.intersult.test.Bean"/>
</e:load>
At line 309 changed 2 lines
!Beispiel
Der Listener kann verwendet werden, um Inhalte von Ergebnislisten bei bedarf neu zu generieren:
Es ist auch möglich, Properties der neuen Bean Werte zuzuweisen:
At line 313 changed 8 lines
@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class QueryController {
@Transactional
@Listener(Rule.EVENT_CHANGED)
public void execute() {
...
}
<e:new name="bean" type="com.intersult.test.Bean">
<f:param name="param1" value="#{some-expression}/>
</e:new>
At line 323 changed one line
__Hinweis:__ Es ist darauf zu achten, dass betreffende Spring-Beans mit einem entsprechenden Scope versehen werden, wie zum Beispiel @Scope(WebApplicationContext.SCOPE_REQUEST). Die Events können nicht während der Startphase der Applikation gebunden werden, da hier [JSF] noch nicht initialisiert ist.
__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.
At line 325 changed 2 lines
!!!Primefaces
[JSF Ext] integriert sich mit einer Reihe von Frameworks und liefert dafür Tools, die teilweise transparent ablaufen.
!!!Tag-Support
[JSF Ext] bietet eine Reihe von Features, die den Bau von eigenen Tags unterstützen.
At line 328 changed 3 lines
* __Richfaces:__ Zusätzlich zu dem Form-Submit Problemen bei [JSF] selbst, treten zusätzliche Render-Probleme im Zusammenhang mit dem AJAX und Richfaces Popups auf. Diese werden durch das in [JSF Ext] enthaltene Javascript ajax.js transparent gelöst. Es können alls Popups und Elemente, die Forms enthalten neu gerendert werden, ohne dass die Submit-Funktionalität von Command-Buttons udn anderen Steuerelementen verloren geht.
* __Spring:__ Wie bereits im Abschnitt über Scopes beschrieben, können auch Spring-Beans für Scopes und andere [JSF Ext] Elemente verwendet werden.
* __Primefaces:__ Primefaces Dialogs <p:dialog> können zusammen mit <e:scope> verwendet werden, um ganz unabhängige Dialoge zu realisieren. Zusätzlich liefert [JSF Ext] eine Lösung zum Anpassen anderer Buttons an das Primefaces Layout.
Composite Tags sind ein mächtiges und zugleich einfach zu handhabendes Instrument zum Erstellen eigener Taglibs. Durch einige zusätzliche Instrumente werden Composite Tags noch vielseitiger. Damit kann vielfach auf die Entwicklung von nativen Tags verzichtet werden, inklusive aufwändiger Compile, Redeploy und Restart-Zyklen. Durch die erweiterten Möglichkeiten steigt gleichzeitig der Anreiz zu Composite Tags.
At line 332 changed 2 lines
!!Primefaces Buttons
Beim Verwenden von [Primefaces] werden <h:inputCommand> und native HTML-Buttons nicht im Primefaces-Style dargestellt. Folgende Einträge korrigieren dies (zumindest für [Primefaces] 3.3):
!!Composite Behaviors
Beim Bau von Composite-Tags möchte man in einigen Fällen auf den darüberliegenden Tag zugreifen.
At line 335 changed one line
# Im pom.xml die jsf-ext eintragen, wie in [JSF Ext|JSF Ext#Ext Konfiguration] beschrieben.
__Hintergrund:__ Die EL-Expression #{component.parent.parent.parent} (dazwischen zwei UINamingContainer und UIPanel die Facelets einfügt) würde normaler Weise den Parent-Tag liefern, evaluiert jedoch zu null. Zum einen ist zu dieser Zeit die Tag-Hierarchie noch nicht aufgebaut, zum anderen wird der UINamingContainer aus der Facelet-Hierarchie herausgenommen, da der Tag durch einen speziellen Mechanismus gerendert wird.
At line 337 changed one line
# Ins Page-Template folgenden Eingrag am Ende des Body-Tags vornehmen:
__Lösung:__
At line 291 added 2 lines
Zugriff über jsf, also #{jsf.tag.composite.parent.component}. Folgendes Beispiel zeigt eine Composite-Component für ein Mouse-Behavior:
At line 341 removed one line
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
At line 296 added one line
xmlns:ui="http://java.sun.com/jsf/facelets"
At line 344 changed 9 lines
...
<h:head id="head">
...
</h:head>
<h:body id="body">
...
<h:outputStylesheet name="primefaces.css" library="css"/>
<h:outputScript name="primefaces.js" library="ext-js"/>
</h:body>
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core"
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:interface>
<cc:implementation>
<e:behavior id="mouseover" event="mouseover" for="#{jsf.tag.composite.parent.component}" script="focus();"/>
<e:behavior id="mouseout" event="mouseout" for="#{jsf.tag.composite.parent.component}" script="blur();"/>
</cc:implementation>
At line 356 changed one line
Danach erscheinen alle <h:commandButton> und andere Buttons im [Primefaces] Style.
Dieser Tag ist bereits Bestandteil des JSF-Ext unter <ext:mouse-focus>. Er kann zusammen mit jedem ClientBehaviorHolder, wie dem <e:div> angewendet werden.
At line 358 changed 2 lines
!!!Validieren
Die Validierung von Eingabewerten ist von der Idee her eine gute Sache, die Konfiguration ist allerdings nicht ganz so leicht. Spätestens wenn in den Faces-Messages die ClientId der betreffenden Komponenten beim Benutzer am Bildschirm erscheinen, hat man das Gefühl es fehlt noch etwas.
!!Forms und AJAX
Wird eine Komponente durch AJAX neu gerendered, die eine FORM enthält, geht der View-State der FORM verloren. In einigen Fällen kann dies verhindert werden, in dem die Form explizit im Render-Attribut eines AJAX-Tags angegeben wird. Im Zusammenhang mit Include-Anweisungen, wie auch den Scopes ist dies nur bedingt möglich.
At line 361 changed one line
Mit [JSF Ext] kann das Ganze so aussehen:
[JSF Ext] löst dieses Problem durch zusätzlichen Java-Script Code. Durch das Einbinden der JAR-Bibliothek ist das Problem transparent gelöst. Der enthaltene Java-Script Code wird immer dann eingebunden, wenn auch der JSF-AJAX-Code eingebunden wird. Er fügt den ViewState in enthaltenenen FORM-Tags ein, sodass keine zusätzlichen Render-Anweisungen benötigt werden.
At line 363 changed one line
[{Image src='JSF Ext/validation.PNG'}]
!!!Events
Events sind eine Erweiterung des Action- und Update-Systems von JSF. Events können von der Java- und XHTML-Seite erzeugt und konsumiert werden.
At line 365 changed one line
Die JSF-Validators werden direkt im Frontend angebracht, also in den XHTML-Dateien. Damit können Felder unterschiedlich validiert werden und Situations abhängige Logik implementiert werden. Zum Beispiel kann gesprüft werden, ob zwei Felder gleich sein müssen. Eine Sonderrolle spielt das Attribut required bei vielen UIInput-Elementen, mit dem eine Eingabe erzwungen werden kann.
||Name||Java||Parameter||Beschreibung
|javax.faces.post_construct|Event.EVENT_POST_CONSTRUCT|Object managedBean|Der Event wird nach dem Erzeugen einer managed Bean durch das JSF-System erzeugt.
|javax.faces.pre_destroy|Event.EVENT_PRE_DESTROY|Der Event wird vor dem Entfernen einer managed Bean erzeugt.
|javax.faces.event|Event.EVENT_EVENT|String event, Object... arguments|Dieser Meta-Event wird bei jedem Event erzeugt, sodass allgemeine Event-Handler geschrieben werden können. Dies ist mit Vorsicht zu verwenden.
|javax.faces.messages|MessageHandler.EVENT|-|Der Event wird erzeugt, wenn Faces Messages vorliegen.
|login.before|Login.EVENT_LOGIN_BEFORE|-|Bevor ein Benutzer eingeloggt wird.
|login.success|Login.EVENT_LOGIN_SUCCESS|-|Nachdem ein Benutzer erfolgreich eingeloggt wurde. Der Benutzer ist über Login.instance().getUser() abrufbar.
|login.fail|Login.EVENT_LOGIN_FAIL|-|Nachdem ein Login fehlgeschlagen ist.
|login.after|Login.EVENT_LOGIN_AFTER|-|Nach dem Login-Prozess, unabhängig ob er erfolgreich oder fehlgeschlagen ist.
|login.logout|Login.EVENT_LOGOUT|-|Nach dem Logout.
At line 367 changed one line
Bean-Validators werden in [Application Support] behandelt.
!!Hintergrund
In [JSF] werden oft Render-Attribute direkt in AJAX- und anderen Anweisungen angegeben. Da diese lose gekoppelt sind, wird im günstigen Fall eine Fehlermeldung entstehen, dass die entsprechende Id nicht mehr im Component Tree vorhanden ist. Der nachträgliche Einbau zusätzlich zu rendernder Components ist nicht vorgesehen, diese müssen explizit am Render-Attribut aller betreffenden Anweisungen hinzugefügt und gepflegt werden.
At line 369 changed 2 lines
!!Unique-Validator
Mit dem Unique-Validator kann ein Property geprüft werden, ob es nur einmal in der Datenbank existiert. Der Unique-Validator braucht den [Application Support] als Abhängigkeit, da auf das Spring-Entity-Management zugegriffen werden muss.
Aus diesem Grund führt [JSF Ext] die (Render-)Events ein. Events können zwar aus der View durch den Tag <e:raise> erzeugt werden, sinnvoll ist meist jedoch die Erzeugung durch die raise-Methode auf der Java-Seite. Dadurch bleibt die lose Kopplung von View und Controller erhalten. Die können Controller können Ereignisse auslösen, auf die sich Teile der View registrieren um neu gerendert zu werden.
At line 339 added 5 lines
!!Java
Ein Event wird erzeugt, indem die Instanz von Event geholt wird. Dies kann durch @ManagedProperty("#{event}") geschehen oder durch die statische Methode Event.instance(). Dabei ist zu beachten, dass keine Session scoped Bean in einen Application Context injected wird.
Das Session basierte Event-Objekt enthält die Methode raise(String event, Object... arguments), also im einfachsten Fall:
At line 373 changed 3 lines
<h:outputLabel for="name" value="Name"/>
<h:inputText id="name" value="#{userEdit.user.name}" required="true"
validator="intersult.Unique"/>
Event.instance().raise("com.intersult.some-event");
At line 378 changed one line
__Erklärung:__ Der Unique-Validator holt sich die Entity-Klasse und Property über die EL-Expression für das Attribut "value" und prüft ob dieser bereits in der Datenbank vorhanden ist. Falls die Bean bereits persistent ist, wird der betreffende Wert außgenommen, sodass die Bean auch mit demselben Wert für das Property geupdatet werden kann.
Die Events werden in der Regel durch eine Annotation konsumiert:
At line 380 changed 2 lines
!!Input-Wrapper
Der Input-Wrapper ist ein Composite-Tag der um ein Input-Element gelegt werden kann. Dadurch werden FacesMessages hinzugefügt und AJAX-Submit ausgeführt, wenn der Wert geändert wird. Das Layout von Eingabeelementen wird dabei vereinheitlicht.
{{{
@Listener("com.intersult.some-event")
public void someEvent() {
}
}}}
At line 383 changed one line
Dabei kann man wählen, welche Teile man benutzen möchte. Ganz bequem geht es mit dem Tag <ext:input>. Damit gewrappte Eingabe-Steuerelemente werden automatisch AJAX-Validiert und mit Faces Messages versehen.
Es ist zu beachten, dass Events nur empfangen werden wenn eine Bean tatsächlich instantiiert ist.
At line 358 added 6 lines
!!FacesMessages
Events können auch im XHTML verarbeitet werden, indem bestimmte Bereiche neu gerendert werden.
!FacesMessages
Zum Beispiel der vorgefertigte Event javax.faces.messages, der bei vorhandenen Faces-Messages ausgelöst wird:
At line 386 changed 6 lines
<h:form id="some-form">
<h:outputLabel for="name:name" value="Name"/>
<ext:input id="name">
<h:inputText id="name" value="#{userEdit.user.name}" required="true"/>
</ext:input>
</h:form>
<h:messages id="messages" globalOnly="true">
<e:render event="javax.faces.messages"/>
</h:message>
At line 394 changed one line
Der Input-Wrapper-Tag ist eine Composite-Component, die man so verwenden kann wie sie ist. Oder man benutzt den Code als Vorlage für ein eigenes Projekt-Layout:
Ergebnis: Die Faces-Messages werden gerendered, ohne dass bei jedem AJAX-Tag ein gesondertes Rendered-Attribut angegeben werden muss.
At line 396 changed 10 lines
{{{
<?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"
!Beispiel Input-Wrapper
__Situation:__ In einer Anwendung kommen zumeist verschiedene Eingabeelemente wie <h:inputText>, <h:selectOneMenu>, <h:inputSecret>, <h:selectBooleanCheckbox> sowie selbst gebaute Tags zur Verwendung. Für die Applikation besteht gewöhnlich ein einheitliches Layout mit AJAX- oder Client-Side Validierung, FacesMessages und weiteren Elementen.
At line 407 changed 15 lines
<cc:interface>
<cc:attribute name="inputId" default=":#{cc.children[0].clientId}"/>
<cc:attribute name="styleClass"/>
<cc:attribute name="style"/>
</cc:interface>
<cc:implementation>
<h:panelGrid cellpadding="0" cellspacing="0" style="#{cc.attrs.style}" styleClass="#{cc.attrs.styleClass}">
<e:insert component="#{cc.children[0]}">
<e:ajax event="change"/>
</e:insert>
<ext:message id="message" for="#{cc.attrs.inputId}"/>
</h:panelGrid>
</cc:implementation>
</html>
}}}
__Lösung bisher:__ In vielen Projekten wird für jedes Input-Element ein Composite-Tag gebaut, in dem der Code für AJAX, Validierung, Messages und so weiter wiederholt wird.
At line 423 changed one line
Der entsprechende Messages-Tag:
Mit [JSF Ext] kann ein generischer Wrapper für Input-Elements gebaut werden:
At line 438 removed one line
<cc:attribute name="for"/>
At line 441 changed 3 lines
<h:outputStylesheet name="faces-messages.css" library="css"/>
<h:message id="message" for="#{cc.attrs.for}" infoClass="msg-info" warnClass="msg-warn" errorClass="msg-error">
<e:render event="#{cc.attrs.for}"/>
<e:insert component="#{cc.children[0]}">
<f:ajax event="change"/>
</e:insert>
<h:message id="#{cc.children[0].id}-message" for=":#{cc.children[0].clientId}">
<e:render event=":#{cc.children[0].clientId}"/>
At line 449 changed one line
Die Styles befinden sich im [JSF Ext] und werden automatisch in den Header geladen.
Die Anwendung dieses Composite-Tags ist dann wie folgt:
At line 451 removed 3 lines
!!Validation Messages
Die ValidationException von JSF nimmt den String direkt. [JSF Ext] stellt eine Ableitung davon zur Verfügung, mit der ein Resource-Key übergeben werden kann. Damit ist eine Internationalisierung möglich:
At line 455 changed one line
throw new ValidatorResourceException("user.unique.name");
<h:form id="form">
<app:input id="text">
<h:inputText id="text" value="#{test.text}"/>
</app:input>
<h:commandButton value="Submit" action="#{bean.action}">
<f:ajax/>
</h:commandButton>
At line 458 changed 3 lines
!!!Converter
!!SelectOneMenu und SelectItem
Das SelectOneMenu und andere Auswahlelemente können nicht direkt mit Objekten umgehen. Eine generische Lösung bietet der intersult.SelectItemConverter:
__Erklärung:__ Die Composite-Component wrappt das Input-Element, welches innerhalb des Composite Tags durch <e:insert> eingefügt wird. Der Zugriff erfolgt dabei durch die EL-Expression cc.children[[0].
At line 418 added 3 lines
!!Component FacesMessages
[JSF Ext] generiert auch Events für Component FacesMessages, die entstehen beim Konvertieren, Validieren oder manuell in Java-Code. Oft wird unnötiger Weise das komplette Form neu gerendert, um die Component FacesMessages anzuzeigen. Mit [JSF Ext] können gezielt einzelne Messages gerendert werden, wenn diese auftreten. Der Event-Name entspricht dabei der Client-Id der aufgetretenen Component FacesMessage:
At line 463 changed 4 lines
<h:selectOneMenu id="start" value="#{workflowEdit.workflow.start}" converter="intersult.SelectItemConverter">
<f:selectItem itemLabel="#{messages['select']}" noSelectionOption="true"/>
<f:selectItems value="#{transitionList.stateList}" var="state" itemLabel="#{state.name}" itemValue="#{state}"/>
</h:selectOneMenu>
<e:render event=":form:some-text"/>
At line 469 changed one line
__Erklärung:__ Der SelectItemConverter nummeriert die SelectItems durch, nutzt dann hashCode- und equals-Methoden um diese wiederzufinden. Bei den Elementen <f:selectItem> kann nun für itemValue die vollwertige Java-Bean angegeben werden, die auch beim Element <h:selectOneMenu> bei value verwendet wird.
Der Event tritt in folgenden Fällen auf:
At line 471 changed one line
__Bemerkung:__ Da für den Browser jedes Element eine Nummer bekommt und nur diese an den Server zurückgeschickt wird, kann es beim zwischenzeitlichen Verändern der Liste zur Auswahl es falschen Elements kommen. Im Regelfall befindet sich die korrekte Liste noch im Component-Tree, sodass der Fehler nicht auftritt.
* Nur wenn die Component vom AJAX-Execute betroffen ist
* Wenn eine FacesMessage für die Component zum ersten Mal vorhanden ist
* Wenn eine FacesMessage für die Component zum zweiten oder öfteren Mal vorhanden ist
* Wenn keine FacesMessage für die Component verschwunden ist
At line 473 changed 2 lines
!!String-Constructor
Der Converter intersult.String ist ein allgemeiner Converter für Objekte, die einen Constructor mit einem String-Argument besitzen und toString implementiert ist. Typischer Weise implementiert man einfache Datentypen auf diese Weise, dazu gehört auch Locale.
!!Raise-Component
Events können hervorragend dazu benutzt werden, um AJAX-Submits und Rerendering voneinander zu trennen. Events werden dabei durch Components erzeugt und von anderen Components konsumiert. So braucht die erzeugende Component zur Erstellungszeit nicht wissen, welche Components rerendered werden:
At line 476 changed 2 lines
!!Language und Country
Die beiden Converter intersult.Language konvertiert ein Locale-Objekt nach dem Language-Anteil als String. Der Converter intersult.Country benutzt den Country-Anteil. Ein Converter für den kompletten Anteil ist nicht erforderlich, da hierfür der Converter intersult.String herangezogen werden kann.
{{{
<h:commandButton value="Submit">
<f:ajax execute="@form"/>
<e:raise name="com.intersult.test"/>
</h:commandButton>
}}}
At line 479 changed 2 lines
!!Display-Language und -Country
Im Gegensatz zu Language- und Country-Converter konvertieren diese Converter einen Language- oder Country-String zu einem lesbaren String.
Hier ist zu beachten, dass ein AJAX-Tag zusätzlich verwendet wird. Bei vollen GET- oder POST-Requests machen Events keinen Sinn, da hier sowieso die komplette Seite gerendered werden würde.
At line 482 changed 3 lines
!!!Files und Resources
!!File Upload
File Upload und Multipart-Requests war schon immer ein wenig behandeltes Thema in JSF. In JSF 2.2 hat man zwar im Zusammenhang mit dem Servlet Standard 3 einen File Upload. Allerdings funktioniert dieser nur ab Tomcat 7 und nicht zusammen mit AJAX.
Das Konsumieren kann wieder auf ähnliche Weise erfolgen:
At line 486 removed 2 lines
Das [JSF Ext] ermöglicht Multipart-Requests und damit auch File Uploads mit Servlet Standard 2. Darüber hinaus ist auch ein Submit mit AJAX möglich. Folgendes Beispiel zeigt die Anwendung:
At line 489 changed 8 lines
<h:form id="form" enctype="multipart/form-data">
<h:inputText id="text" value="#{fileUpload.text}"/>
<e:inputFile id="file" value="#{fileUpload.file}" filename="#{fileUpload.filename}"
mimeType="#{fileUpload.mimeType}"/>
<h:commandButton value="Save" action="#{fileUpload.save}">
<f:ajax execute="@form"/>
</h:commandButton>
</h:form>
<h:outputText id="output" value="#{bean.text}">
<e:render event="com.intersult.test"/>
</h:outputText>
At line 499 changed one line
Der Tag <e:inputFile> hat folgende EL-Parameter:
Es ist zu beachten, dass bei der Update-Component (hier outputText) eine Id angegeben wird, damit der AJAX-Handler das Rerendering zuordnen kann. Dies tritt beim Verwenden des render-Attributs nicht auf, da durch die Angabe einer Id ja eine vergeben wurde.
At line 501 changed 4 lines
||Name||Jave-Typ||Beschreibung
|value|byte[]|Enthält den Inhalt der Datei. Bei Bedarf wird noch eine Stream-Lösung geliefert.
|filename|String|Enthält den Filenamen der Datei.
|mimeType|String|Enthält den vom Browser gesendeten Mime-Type.
!!Hinweise
Events sind momentan Session-basiert, das heißt es existieren keine Application übergreifenden Events.
At line 506 changed 2 lines
!!Resource Provider
Ab JSF 2 ist es zwar möglich Resource Handler von dem Interface ResourceHandler abzuleiten. Allerdings erfordert die Implementierung weitere Klassen und eine Vielzahl von Methoden, einen Faces Wrapper und das Registrieren im Faces Context.
!!!Scopes
Scopes sind Bereiche, innerhalb derer Variablen zugreifbar sind und es kann ein Facelet zugeordnet sein. Die Variablen sind über EL-Expressions durch #{scope.<...>} zugreifbar. Insbesondere können sie durch den Load-Tag mit <f:param> übergeben werden. So entfallen iterierte Popups, Kontext-Menüs und andere Detail-Views.
At line 509 changed one line
Eleganter geht es mit der Annotation @ResourceProvider, diese verwandelt die Methode einer Spring-Bean zu einem Resource Handler:
!!Einfacher Scope
Der Scope beginnt mit einem Scope-Tag:
{{{
<e:scope id="scope-id">
<h:outputText value="#{scope.id}"/>
</e:scope>
}}}
At line 468 added 3 lines
!!Scope mit Facelet
Die Scopes sind eines der mächtigsten Features im [JSF Ext]. Ein Scope wird zunächst im XHTML definiert:
At line 512 changed 11 lines
@ResourceProvider("file")
public boolean handleResourceRequest(String resourceName) throws IOException {
if (file == null)
return false;
FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getResponseOutputStream().write(file);
context.getExternalContext().setResponseContentType(mimeType);
context.getExternalContext().setResponseContentLength(file.length);
context.responseComplete();
return true;
}
<e:scope id="popup" viewId="/popup.xhtml"/>
At line 525 changed one line
__Erklärung:__ Hier wirde explizit der Library-Name "file" angegeben, da die Methode nur auszugsweise gezeigt wird. Dieser Wert kann weggelassen werden, dann wird der Name der Spring-Bean verwendet. Dies vereinfacht auch das Wiederfinden eines Resource Providers.
Diese Definition fügt einen Scope in den Component-Tree ein. Der Scope ist ein UINamingContainer, die Id wird also zu jeder enthaltenen Komponente als Prefix vorangestellt. Zum Beispiel erzeugt <h:inputText id="name" value="#{bean.name}"/> die Ausgabe von <input name="popup:name">.
At line 527 changed one line
__Hinweis:__ Dieses Features verwendet zusätzlich [Application Support] und [JSF Spring Integration]. In der Regel wird es sich um ein Singleton (SCOPE_SINGLETON) handeln, also dem Default-Scope für eine Spring-Bean. Es können allerdings auch andere Scopes verwendet werden, wie Session Scope, beispielsweise um Nutzer spezifische Daten vorhalten zu können.
Dieser Scope verweist auf die viewId "/popup.xhtml", damit ist ein Facelet innerhalb der Web-Applikation gemeint, vergleichbar zu <ui:include>. Das Facelet wird allerdings (zunächst) nicht geladen, im Komponentenbaum befindet sich ausschließlich die Scope Component.
At line 529 changed one line
__Achtung:__ Es gibt Servlet-Filter, welche den InputStream des HttpServletRequest nicht korrekt durchreichen. Sie sollten diese Filter ausschalten, sonst kann der File-Upload nicht an die entsprechende Datei herankommen.
!!Scope laden
Zum passenden Zeitpunkt soll der Scope natürlich geladen werden, das heißt das mit viewId verwiesene Facelet wird tatsächlich in den Component-Tree eingefügt und per AJAX an den Client übertragen. Dies geschieht mit:
At line 531 changed 2 lines
!!!Utilities und Tools
[JSF Ext] enthält einige Instrumente zur Unterstützung der Entwicklung von weiteren Tool- und Component-Bibliotheken für [JSF].
{{{
<e:load scopeId="popup"/>
}}}
At line 534 changed 2 lines
!!Document Writer
[JSF] bietet die Möglichkeit zum Einsatz von Render-Kits, also eine alternative Darstellung der XHTML-Seiten. Der Standard-Writer schreibt allerdings HTML-Code direkt als Stream. Für Anwendung wie z.B. PDF generieren wäre sinnvoll, auf den Document-Tree zuzugreifen. Dafür kann der Document Writer eingesetzt werden:
Das Load-Tag ist ein ActionListener, wird also unterhalb einer ActionSource eingefügt, wie zum Beispiel das <f:actionListener> oder <f:setPropertyActionListener>. Im Zusammenspiel mit AJAX kann nun der Scope dynamisch geladen werden:
At line 538 changed 2 lines
DocumentWriter writer = new DocumentWriter(context.getResponseWriter(), "application/pdf");
context.setResponseWriter(writer);
<h:commandButton id="load-popup" value="Load Popup">
<f:ajax/>
<e:load scopeId="popup"/>
</h:commandButton>
At line 542 changed one line
__Hinweis:__ Für die Integration in den JSF Lifecycle sind noch einige andere Dinge zu beachten. Dies zu erklären wäre zu umfangreich für diese Dokumentation, dies kann in der Dokumentation für JSF nachgelesen werden.
Beim Laden des Scopes können zusätzlich Parameter übergeben werden:
At line 544 changed 2 lines
!!XML Rendering
Soll die Seite direkt als XML gerendert werden, bietet [JSF Ext] noch eine weitere Kapselung des Document Writer an. Die Methode Jsf.renderViewXml kann direkt aus einem HTTP-Request ein XHTML-Document rendern, ohne über das JSF-Servlet zu gehen.
{{{
<h:commandButton id="load-popup" value="Load Popup">
<f:ajax render=":popup:form"/>
<e:load scopeId="popup">
<f:param name="someParam" value="someValue"/>
<f:param name="someExpression" value="#{bean.someValue}"/>
</e:load>
</h:commandButton>
}}}
At line 547 changed one line
Dies kann nützlich sein, wenn der Document-Tree weiter verarbeitet werden soll, zum Beispiel zum Generieren anderer Ansichten.
Das geladene Popup-Facelet "/popup.xhtml" kann zum Beispiel so aussehen:
At line 549 changed one line
__Hinweis:__ Für PDF steht das [PDF Renderkit] zur Verfügung. Andere Browser- und Download-Ansichten sollten generell in der Form eines Renderkit zur Verfügung gestellt werden. Für weitere Unterstützung beraten wir Sie gerne.
{{{
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:t="http://siemens.com/test"
xmlns:app="http://java.sun.com/jsf/composite/app"
xmlns:e="http://java.sun.com/jsf/ext"
xmlns:ext="http://java.sun.com/jsf/composite/ext"
>
<rich:popupPanel id="popup" show="true" autosized="true" modal="false">
<f:facet name="header">
<h:outputText value="Loaded Popup"/>
</f:facet>
<f:facet name="controls">
<h:form id="controls-form">
<h:commandButton value="X" immediate="true">
<f:ajax/>
<e:unload/>
<e:behavior script="#{rich:component('popup')}.hide(event);"/>
</h:commandButton>
</h:form>
</f:facet>
<h:form id="form">
<h:panelGrid columns="2">
<h:outputLabel for="popupText" value="Popup Text"/>
<h:outputText id="popupText" value="#{bean.popupText}"/>
<h:outputLabel for="someParam" value="Some Param"/>
<h:outputText id="someParam" value="#{scope.someParam}"/>
<h:outputLabel for="someExpression" value="Some Expression"/>
<h:inputText id="someExpression" value="#{scope.someExpression}"/>
</h:panelGrid>
<ext:buttons>
<h:commandButton id="save" value="Save" action="#{bean.save}">
<f:ajax execute="@form"/>
<e:unload/>
<e:behavior script="#{rich:component('popup')}.hide(event);"/>
</h:commandButton>
<h:commandButton id="cancel" value="Cancel" immediate="true">
<f:ajax/>
<e:unload/>
<e:behavior script="#{rich:component('popup')}.hide(event);"/>
</h:commandButton>
</ext:buttons>
</h:form>
</rich:popupPanel>
</ui:composition>
}}}
At line 551 changed 2 lines
!!Unwrapping FacesWrapper
Vor allem seit [JSF 2|JSF2] wurde die faces-config.xml weiter ausgebaut. JSF-Komponenten können viele JSF-Klassen wrappen, indem sie das Interface FacesWrapper implementieren. Einige Klassen haben diesen Wrapper bereits implementiert, sodass sie nur noch abgeleitet werden brauchen. [JSF Ext] enthält noch weitere Wrapper im Package com.intersult.jsf.wrapper.*, wie zum Beispiel ViewRootWrapper oder ValueExpressionWrapper, die das Implementieren von Komponenten unterstützen können.
Hier ist auch das Unload-Tag enthalten:
At line 554 changed one line
Dahinter liegender Sinn ist das Zusammenspiel mehrerer Faces-Erweiterungen nebenher. Die Wrapper werden in unbestimmter Reihenfolge aufgerufen, sodass jede betroffene Klasse prüfen kann, ob sie etwas zu tun hat. Falls nicht, gibt sie den Aufruf einfach an die Wrapped Instance weiter.
{{{
<h:commandButton value="X">
<f:ajax/>
<e:unload/>
<e:behavior script="#{rich:component('popup-panel')}.hide(event);"/>
</h:commandButton>
}}}
At line 556 changed one line
Einen Nachteil gibt es bei der Sache: Hat man FacesWrapper erst einmal abgeleitet, kommt man meist nur mehr sehr schwierig an die eigene Klasse ran. Abhilfe schafft die Methode Jsf.unwrap, mit der man seine eigenen (oder auch fremde) Klassen in der Wrapper-Kette findet:
Es handelt sich wie beim Unload-Tag um einen commandButton, der mit AJAX-Tag erweitert wurde. Im Gegensatz zum Load-Tag braucht beim Unload-Tag nicht zwingend die Scope-Id angegeben werden. Für den Fall dass der Unload-Tag innerhalb eines Scopes auftritt, wird automatisch dieser Scope verwendet. Damit vereinfachen sich unter anderem Close- und Save-Buttons.
At line 575 added 3 lines
!!Scopes entladen
Neben dem Tag <e:unload> können Scopes auch mit #{scopes.unload(id)} entladen werden und innerhalb des Scopes selbst mit #{scope.unload}. Dies ist praktisch, um zum Beispiel ohne Formular einen Close-Butten auf einem Popup zu platzieren:
At line 559 changed one line
ExtFacesContext extContext = Jsf.unwrap(parent, ExtFacesContext.class);
<p:dialog id="dialog" header="#{messages['rule.edit']}" visible="true">
<f:ajax event="close" listener="#{scope.unload}"/>
<h1>Content</h1>
</p:dialog>
At line 562 changed 2 lines
!!Expression Analyzer
Methoden um Referenzen auf EL-Expressions zu bekommen, Expressions umzuwandeln. Wird gebraucht, um Validierungen an der Oberfläche vornehmen zu können.
!!Java-Code
Wird innerhalb des XHTML eine Java-Action aus einem Scope aufgerufen, so ist der Scope zugreifbar durch Scopes.getScope()
At line 588 added 2 lines
Ein Klasse im Custom-Scope kann so aussehen:
At line 566 changed 2 lines
ValueReference reference = ExpressionAnalyzer.getReference(expression, context.getELContext());
Object base = reference.getBase();
@ManagedBean
@CustomScoped("#{scopes.scope}")
public class FieldEdit implements Serializable {
private static final long serialVersionUID = 1L;
@ManagedProperty("#{scopes.scope.value}")
private Field field;
public Field getField() {
if (field == null)
field = new Field();
return field;
}
public void setField(Field field) {
this.field = field;
}
public void save() {
ProcessService.saveField(field);
Event.instance().raise("workflow." + field.getWorkflow().getId() + ".fieldList.change");
Resource.addMessage(
FacesMessage.SEVERITY_INFO, "field.save.success", field.getName(), field.getWorkflow().getName());
Scopes.instance().getScope().unload();
}
}
At line 570 changed 2 lines
!!Interpolator
Mit dem Interpolator können EL-Expressions innerhalb eines Strings ersetzt werden.
Hier spart die save-Methode eine Menge Code, bei erfolgreicher Durchführung wird durch Scopes.instance().getScope().unload() der Scope beendet, die enthaltenen Daten freigegeben zur Garbage Collection sowie ein dahinerliegendes Popup geschlossen. Es sind keine weiteren XHTML-Elemente erforderlich, da der Render-Event dadurch ebenfalls erzeugt wird.
At line 573 changed 12 lines
||Methode||Bedeutung
|interpolate|Einen String interpolieren.
|evaluate|Eine Expression evaluieren.
|create|Eine neue EL-Expression anlegen.
|createMethodExpression|Eine Method-Expression erzeugen.
|createValueExpression|Eine Value-Expression erzeugen.
|createLiteral|Eine Literal-Expression erzeugen.
|coerce|Eine Typumwandlung vornehmen.
|setVariable|Eine Variable setzen.
|restoreVariable|Eine Variable wiederherstellen.
|action|Eine Action-Expression ausführen.
|converterFor|Einen Converter für eine bestimmte Klasse holen.
!!Spring
Der Scope kann auch zusammen mit Spring-Scopes verwendet werden, dabei ist die Konfiguration weiter unten in diesem Dokument zu beachten.
At line 586 changed 2 lines
!!IO Utils
Methoden für das Schreiben, Lesen und Kopieren von Streams:
{{{
@Component
@Scope(Scopes.SCOPE_NAME)
public class SomeBean {
...
}
}}}
At line 631 added 5 lines
__Erklärung:__ Die Beans werden also bei Bedarf instantiiert und im JSF-Scope abgelegt. Dadurch kann ein Dialog- oder Unterdialog abgearbeitet werden. Am Ende des [Workflow] wird der Scope samt Inhalt wieder abgeräumt und der Garbage-Collection zugeführt. Dies ist sehr effizient implementiert, die Anwendung profitiert durch Verwenden von <e:load>, <e:unload> und <e:scope> von einer spürbaren Vereinfachung.
!!Verschachtelte Scopes
Der Code demonstriert das Iterieren und Schachteln von Scopes:
At line 590 changed one line
IOUtils.copy(binaryStream, outputStream);
<c:forEach begin="1" end="3" var="index">
<e:scope id="scope-#{index}" load="true">
<f:param name="scopeVar" value="scope-value-#{index}"/>
<div style="padding: 3px; margin: 3px; background-color: #e0e0e0; width: 400px;">
<h:outputText value="Inside: #{scope.id}, #{scope.scopeVar}"/>
<e:scope id="nested" load="true">
<f:param name="innerVar" value="value-#{index}-#{scope.parent.scopeVar}"/>
<h:outputText value="Nested: #{scope.innerVar}"/>
</e:scope>
</div>
</e:scope>
</c:forEach>
At line 593 changed 2 lines
!!!Navigation
[JSF Ext] enthält eine transparente Lösung für das Redirect-After-Submit-Pattern. Am besten arbeitet diese Lösung mit web.xml 3.0 und Application-Servern, wie Tomcat 7, die diesen Standard unterstützen.
In der Praxis würde man eher Facelets laden mit dem Load-Tag und kein festes Attribut load="true" angeben.
At line 596 changed one line
__Hintergrund:__ Ohne Redirect-After-Submit wird nach der Navigation auf eine andere Seite in der Browser-URL-Zeile die vorhergehende Seite angezeigt. Des Weiteren wird bei einem Seiten-Request die für den Benutzer oft unverständliche Meldung angezeigt, ob die Seite nochmal abgeschickt werden soll. Ziel des Redirect-After-Submit-Pattern ist beides zu vermeiden.
!!!Spring Framework Integration
[JSF Ext] arbeitet mit dem Spring Framework zusammen. Für die Spring-JSF-Integration ist es sinnvoll zunächst den EL-Resolver von Spring zu registrieren:
At line 598 changed one line
Redirect-After-Submit wird aktiv, wenn man [JSF Ext] einbindet und web.xml 3.0 verwendet. Alle Page-Submits mit <h:commandButton>, <h:commandLink> etc. werden mit einem Redirect abgeschlossen.
{{{
<faces-config>
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
</faces-config>
}}}
At line 600 changed one line
__Ergebnis:__ Nach der Navigation wird die aktuelle URL im Browser angezeigt. Refresh der Seite im Browser kann ohne Rückfrage durchgeführt werden.
!!Scopes
Wenn man nicht nur JSF Managed Beans im Custom Scope von [JSF Ext] haben möchte, kann dieser auch in der applicationContext.xml registriert werden:
At line 602 changed one line
__Hinweis:__ Bei einem Redirect geht üblicher Weise der Zustand der Seite verloren. [JSF Ext] nutzt den sogenannten Flash-Scope von [JSF], um dieses Hindernis zu überwinden und auch Faces-Messages über den Redirect zu retten. Daher ist web.xml 3.0 und Tomcat 7 für das saubere Arbeiten erforderlich.
{{{
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="view">
<bean class="com.intersult.jsf.spring.ViewScope"/>
</entry>
<entry key="scope">
<bean class="com.intersult.jsf.spring.CustomScope"/>
</entry>
</map>
</property>
</bean>
}}}
At line 604 changed one line
Bei web.xml 2.x und Tomcat 6 kann ein Fallback-Mechanismus verwendet werden, der die View-Id als GET-Parameter an die URL hängt. Dies ist zwar problemlos möglich, jedoch können dann keine "sauberen" URLs mehr produziert werden. Dieses Verhalten kann mit einem Konfigurations-Parameter in der web.xml aktiviert werden:
__Hinweis:__ [JSF Ext] enthält zusätzlich noch einen Scope, mit dem der ViewScope von JSF registriert werden kann.
At line 684 added 2 lines
Um Werte aus Scopes in Beans zu injizieren, können die Scopes
At line 607 changed 4 lines
<context-param>
<param-name>javax.faces.REDIRECT_AFTER_SUBMIT</param-name>
<param-value>true</param-value>
</context-param>
<bean name="scopes" class="com.intersult.jsf.Scopes" factory-method="instance" scope="session" lazy-init="true"/>
<bean name="scope" factory-bean="scopes" factory-method="getScope" scope="prototype" lazy-init="true"/>
At line 613 changed 2 lines
!!!Fehler und Exceptions
[JSF Ext] enthält mehrere Mechanismen für das Handling von Exceptions. Grundlage dazu ist der ExtExceptionHandler und eine speziell Ableitung, der AjaxExceptionHandler.
!Beispiel
Zum Beispiel wäre dann folgende Konstruktion möglich:
At line 616 changed 2 lines
!!AJAX Exception Handler
Der Handler ist dafür verantwortlich, dass Exceptions während eines AJAX-Requests nicht die komplette Seite lahm legen. Eine derartige Exception wird ins Logfile eingetragen und eine Faces-Message dafür generiert.
Beim Laden des Scopes:
At line 619 changed one line
Dabei existieren zwei Konfigurationsparameter:
{{{
<e:load scopeId="rule-remove">
<f:param name="rule" value="#{rule}"/>
</e:load>
}}}
At line 621 changed 3 lines
||Parameter||Bedeutung
|javax.faces.IGNORE_EXCEPTIONS|Eine durch Kommata separierte Liste von voll qualifizierten Exception-Klassen-Namen, die ignoriert werden sollen. Diese tauchen nicht als Fehler auf. Damit sollte sehr vorsichtig umgegangen werden, weil es zu versteckten Fehlfunktionen in der Applikation führen kann, die später sehr schwer zu finden sein können.
|javax.faces.PASS_EXCEPTIONS|Diese Exceptions werden an den nächsten Exception-Handler durchgelassen. Dies ist für Exceptions sinnvoll, für die man einen eigenen Exception-Handler schreiben möchte oder eine Dritt-Software hat, die diese handeln möchte.
Innerhalb des Scopes:
At line 625 changed 2 lines
!!Full Request Exception Handler
Wenn kein AJAX-Request vorliegt oder die Exception in javax.faces.PASS_EXCEPTIONS konfiguriert wurde und von keinem anderen Exception-Handler verarbeitet wurde, erreicht sie den ExtExceptionHandler.
{{{
<h:commandButton value="#{messages['remove']}" action="#{ruleEditController.remove}">
<f:ajax/>
<e:unload/>
</h:commandButton>
}}}
At line 628 changed one line
Dieser erlaubt das Definieren von Exception-Navigationen in der faces-config.xml:
Der Controller dazu:
At line 631 changed 12 lines
<navigation-rule>
<navigation-case>
<from-outcome>org.springframework.security.access.AccessDeniedException</from-outcome>
<to-view-id>/security/login.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<navigation-case>
<from-outcome>java.lang.Throwable</from-outcome>
<to-view-id>/error.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class RuleEditController {
@Value("#{scope.rule}")
private Rule rule;
@Transactional
public void remove() {
...
}
}
At line 645 changed one line
__Erklärung:__ Die erste Navigationsregel leitet alle Fälle von AccessDeniedException auf die View /security/login.xhtml um. Die zweite Regel für Throwable leitet alle Exceptions, die nicht von vorherigen Regeln erfasst wurden auf die View /error.xhtml um.
__Erklärung:__ Damit ist es möglich, eine Entity- oder andere Modell-Bean innerhalb eines Scopes zu bearbeiten, dessen Lebenszyklus an ein AJAX-Popup gekoppelt ist. Es ist keine weitere Java-Bean erforderlich, die in einem Scope abgelegt werden muss. Alle parameterisierten Beans werden beim Beenden (Unload) des Scopes freigegeben.
At line 647 changed one line
Die aufgetretene Exception wird dabei in einer Bean namens pageError erfasst:
__Hinweis:__ Der Controller ist mit der Annotation @Scope(WebApplicationContext.SCOPE_REQUEST), damit die Injection im richtigen Scope erfolgt. Der Scope kann sich mit jedem Request ändern.
At line 649 changed 5 lines
||Property oder Methode||Bedeutung
|exception|Hier ist die aufgetretene Instanz von java.lang.Throwable enthalten. Dadurch können genauere Informationen über den Fehler angezeigt werden.
|viewId|Hier ist die View-Id gespeichert, bei Bedarf kann diese ebenfalls angezeigt werden.
|init()|Diese Methode kann vor dem Aufbau der Error-Page aufgerufen werden und check ob ein Fehler vorliegt.
|back()|Navigiert zurück auf die Seite auf der der Fehler aufgetreten war, damit der Benutzer weiter arbeiten kann.
!!Events
Um die Annotation @Listener auch in Spring-Beans verwenden zu können, kann man den EventBeanProcessor in Spring registrieren:
At line 655 changed 2 lines
!!!Downloads
* [JSF Ext CDI Package|JSF Ext/jsf-ext-cdi.zip]
{{{
<bean class="com.intersult.jsf.spring.EventBeanProcessor"/>
}}}
At line 658 changed 2 lines
!!!Rechliches
JSF ist eine Wort- und Bildmarke, auf die keinerlei Anspruch erhoben wird. Die Nennung dieser Marke dient ausschließlich der Referenz und Zuordnung dieses Produkts.
!Beispiel
Der Listener kann verwendet werden, um Inhalte von Ergebnislisten bei bedarf neu zu generieren:
{{{
@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class QueryController {
@Transactional
@Listener(Rule.EVENT_CHANGED)
public void execute() {
...
}
}}}
__Hinweis:__ Es ist darauf zu achten, dass betreffende Spring-Beans mit einem entsprechenden Scope versehen werden, wie zum Beispiel @Scope(WebApplicationContext.SCOPE_REQUEST). Die Events können nicht während der Startphase der Applikation gebunden werden, da hier [JSF] noch nicht initialisiert ist.