Einführung
JSTL- <c:xxx>
Tags sind alle Taghandler und werden während der Erstellungszeit der Ansicht ausgeführt , während JSF- <h:xxx>
Tags alle UI-Komponenten sind und während der Renderzeit der Ansicht ausgeführt werden .
Beachten Sie, dass von JSF eigenen <f:xxx>
und <ui:xxx>
Tags nur diejenigen , die ihnen nicht aus erstrecken UIComponent
auch taghandlers, zum Beispiel <f:validator>
, <ui:include>
, <ui:define>
usw. Diejenigen , die aus erstrecken UIComponent
auch JSF UI - Komponenten, zum Beispiel <f:param>
, <ui:fragment>
, <ui:repeat>
usw. Von JSF UI - Komponenten nur die id
und binding
Attribute sind Wird auch während der Erstellungszeit der Ansicht ausgewertet. Daher gilt die folgende Antwort zum JSTL-Lebenszyklus auch für die Attribute id
und binding
von JSF-Komponenten.
Die Ansicht Kompilierzeit ist , dass , wenn das Moment XHTML / JSP - Datei analysiert werden soll , und zu einer JSF Komponentenstruktur umgewandelt , die dann als gespeichert wird UIViewRoot
von den FacesContext
. Die Renderzeit für die Ansicht ist der Moment, in dem der JSF-Komponentenbaum HTML generiert, beginnend mit UIViewRoot#encodeAll()
. Also: JSF-UI-Komponenten und JSTL-Tags werden nicht synchron ausgeführt, wie Sie es von der Codierung erwarten würden. Sie können es wie folgt visualisieren: JSTL wird zuerst von oben nach unten ausgeführt, wobei der JSF-Komponentenbaum erstellt wird. Anschließend wird JSF erneut von oben nach unten ausgeführt und die HTML-Ausgabe erstellt.
<c:forEach>
vs. <ui:repeat>
Beispiel: Dieses Facelets-Markup iteriert über 3 Elemente mit <c:forEach>
:
<c:forEach items="#{bean.items}" var="item">
<h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>
... erstellt während der Erstellungszeit der Ansicht drei separate <h:outputText>
Komponenten im JSF-Komponentenbaum, die ungefähr so dargestellt werden:
<h:outputText id="item_1" value="#{bean.items[0].value}" />
<h:outputText id="item_2" value="#{bean.items[1].value}" />
<h:outputText id="item_3" value="#{bean.items[2].value}" />
... die wiederum ihre HTML-Ausgabe während der Renderzeit der Ansicht individuell generieren:
<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>
Beachten Sie, dass Sie die Eindeutigkeit der Komponenten-IDs manuell sicherstellen müssen und dass diese auch während der Erstellungszeit der Ansicht ausgewertet werden.
Während dieses Facelets-Markup über 3 Elemente iteriert <ui:repeat>
, ist dies eine JSF-UI-Komponente:
<ui:repeat id="items" value="#{bean.items}" var="item">
<h:outputText id="item" value="#{item.value}" />
</ui:repeat>
... landet bereits unverändert im JSF-Komponentenbaum, wobei dieselbe <h:outputText>
Komponente während der Renderzeit der Ansicht wiederverwendet wird , um eine HTML-Ausgabe basierend auf der aktuellen Iterationsrunde zu generieren:
<span id="items:0:item">value1</span>
<span id="items:1:item">value2</span>
<span id="items:2:item">value3</span>
Beachten Sie, dass <ui:repeat>
als NamingContainer
Komponente bereits die Eindeutigkeit der Client-ID basierend auf dem Iterationsindex sichergestellt wurde. Es ist auch nicht möglich, EL als id
Attribut für untergeordnete Komponenten auf diese Weise zu verwenden, da es auch während der Erstellungszeit der Ansicht ausgewertet wird, während #{item}
es nur während der Renderzeit der Ansicht verfügbar ist. Gleiches gilt für eine h:dataTable
und ähnliche Komponenten.
<c:if>
/ <c:choose>
vs.rendered
Als weiteres Beispiel fügt dieses Facelets-Markup unter bestimmten Bedingungen verschiedene Tags hinzu <c:if>
(Sie können dies auch verwenden <c:choose><c:when><c:otherwise>
):
<c:if test="#{field.type eq 'TEXT'}">
<h:inputText ... />
</c:if>
<c:if test="#{field.type eq 'PASSWORD'}">
<h:inputSecret ... />
</c:if>
<c:if test="#{field.type eq 'SELECTONE'}">
<h:selectOneMenu ... />
</c:if>
... type = TEXT
fügt nur die <h:inputText>
Komponente zum JSF-Komponentenbaum hinzu:
<h:inputText ... />
Während dieses Facelets-Markup:
<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />
... wird unabhängig von der Bedingung genau wie oben im JSF-Komponentenbaum angezeigt. Dies kann daher zu einem "aufgeblähten" Komponentenbaum führen, wenn Sie viele davon haben und diese tatsächlich auf einem "statischen" Modell basieren (dh das field
ändert sich zumindest während des Ansichtsbereichs nie). Außerdem können EL- Probleme auftreten, wenn Sie sich mit Unterklassen mit zusätzlichen Eigenschaften in Mojarra-Versionen vor 2.2.7 befassen.
<c:set>
vs. <ui:param>
Sie sind nicht austauschbar. Die <c:set>
Sätze eine Variable in dem EL - Umfang, der nur zugänglich ist , nach dem Tag Lage während Ansicht Kompilierzeit, sondern überall in der Ansicht während Ansicht Zeit machen. Die <ui:param>
übergibt ein EL - Variable auf eine Facelet Vorlage über enthalten <ui:include>
, <ui:decorate template>
oder <ui:composition template>
. Ältere JSF-Versionen hatten Fehler, bei denen die <ui:param>
Variable auch außerhalb der fraglichen Facelet-Vorlage verfügbar ist. Darauf sollte man sich niemals verlassen.
Das <c:set>
ohne scope
Attribut verhält sich wie ein Alias. Das Ergebnis des EL-Ausdrucks wird in keinem Bereich zwischengespeichert. Es kann daher perfekt im Inneren verwendet werden, um beispielsweise JSF-Komponenten zu iterieren. So wird zB unten gut funktionieren:
<ui:repeat value="#{bean.products}" var="product">
<c:set var="price" value="#{product.price}" />
<h:outputText value="#{price}" />
</ui:repeat>
Es ist nur nicht geeignet, um beispielsweise die Summe in einer Schleife zu berechnen. Verwenden Sie dazu stattdessen den EL 3.0-Stream :
<ui:repeat value="#{bean.products}" var="product">
...
</ui:repeat>
<p>Total price: #{bean.products.stream().map(product->product.price).sum()}</p>
Nur, wenn Sie das Set - scope
Attribut mit einem der zulässigen Werten request
, view
, session
, oder application
, dann wird es sofort in dem angegebenen Bereich während der Ansichtserstellungszeit und gespeichert ausgewertet werden.
<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />
Dies wird nur einmal ausgewertet und ist für #{dev}
die gesamte Anwendung verfügbar .
Verwenden Sie JSTL, um die Erstellung von JSF-Komponentenbäumen zu steuern
JSTL verwenden , kann nur zu unerwarteten Ergebnissen führen , wenn sie innerhalb JSF Iterieren Komponenten wie verwendet werden <h:dataTable>
, <ui:repeat>
usw., oder wenn JSTL - Tag Attribute sind abhängig von Ergebnissen der JSF Ereignisse wie preRenderView
oder übermittelten Formularwerte in dem Modell , die während der Ansicht Kompilierzeit nicht verfügbar sind . Verwenden Sie JSTL-Tags daher nur, um den Ablauf der JSF-Komponentenbaumerstellung zu steuern. Verwenden Sie JSF-UI-Komponenten, um den Fluss der HTML-Ausgabegenerierung zu steuern. Binden Sie die var
iterierenden JSF-Komponenten nicht an JSTL-Tag-Attribute. Verlassen Sie sich nicht auf JSF-Ereignisse in JSTL-Tag-Attributen.
Immer wenn Sie der Meinung sind, dass Sie eine Komponente über an die Backing Bean binden binding
oder eine über greifen findComponent()
und ihre untergeordneten Elemente mithilfe von Java-Code in einer Backing Bean erstellen und bearbeiten müssen new SomeComponent()
und was nicht, sollten Sie sofort aufhören und stattdessen die Verwendung von JSTL in Betracht ziehen. Da JSTL auch XML-basiert ist, wird der Code, der zum dynamischen Erstellen von JSF-Komponenten benötigt wird, viel besser lesbar und wartbar.
Es ist wichtig zu wissen, dass Mojarra-Versionen, die älter als 2.1.18 sind, einen Fehler beim teilweisen Speichern des Status hatten, wenn auf eine Bean mit Ansichtsbereich in einem JSTL-Tag-Attribut verwiesen wurde. Die Bean mit dem gesamten Ansichtsbereich wird neu erstellt, anstatt aus dem Ansichtsbaum abgerufen zu werden (einfach, weil der vollständige Ansichtsbaum zum Zeitpunkt der Ausführung von JSTL noch nicht verfügbar ist). Wenn Sie einen Status in der Bean mit Ansichtsbereich durch ein JSTL-Tag-Attribut erwarten oder speichern, wird der erwartete Wert nicht zurückgegeben, oder er geht in der Bean mit realem Ansichtsbereich "verloren", die nach der Ansicht wiederhergestellt wird Baum wird gebaut. Falls Sie kein Upgrade auf Mojarra 2.1.18 oder neuer durchführen können, müssen Sie das Speichern des teilweisen Status web.xml
wie folgt deaktivieren :
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>false</param-value>
</context-param>
Siehe auch:
In den folgenden Fragen / Antworten finden Sie einige Beispiele aus der Praxis, in denen JSTL-Tags hilfreich sind (dh wenn sie beim Erstellen der Ansicht wirklich richtig verwendet werden):
In einer Nussschale
Wenn Sie JSF-Komponenten bedingt rendern möchten , verwenden Sie rendered
stattdessen das Attribut für die JSF-HTML-Komponente, insbesondere wenn es sich #{lpc}
um das aktuell iterierte Element einer JSF-iterierenden Komponente wie <h:dataTable>
oder handelt <ui:repeat>
.
<h:someComponent rendered="#{lpc.verbose}">
...
</h:someComponent>
Oder, wenn Sie wollen Build (Erstellen / Hinzufügen) JSF - Komponenten bedingt, dann halten JSTL verwenden. Es ist viel besser als wörtlich new SomeComponent()
in Java.
<c:if test="#{lpc.verbose}">
<h:someComponent>
...
</h:someComponent>
</c:if>
Siehe auch:
<ui:repeat>
es sich um einen Tag-Handler handelt (aufgrund dieser Zeile " Beachten Sie, dass JSF eigene<f:xxx>
und<ui:xxx>
... "), der genau wie<c:forEach>
und daher zum Zeitpunkt der Erstellung der Ansicht ausgewertet wird (wieder genau wie<c:forEach>
). . Wenn es das ist, sollte es keinen sichtbaren funktionalen Unterschied zwischen<ui:repeat>
und geben<c:forEach>
? Ich verstehe nicht, was genau dieser Absatz bedeutet :)<f:xxx>
und<ui:xxx>
nicht erweiterte TagsUIComponent
auch Tag-Handler sind. " Versucht zu implizieren, dass dies<ui:repeat>
auch ein Tag-Handler ist, weil er<ui:xxx>
auch enthält<ui:repeat>
? Dies sollte dann bedeuten, dass dies<ui:repeat>
eine der Komponenten ist<ui:xxx>
, die sich ausdehnenUIComponent
. Daher ist es kein Tag-Handler. (Einige von ihnen können möglicherweise nicht erweitert werdenUIComponent
. Daher sind sie Tag-Handler.) Ist das so?<c:set>
ohnescope
erstellt einen Alias des EL-Ausdrucks, anstatt den ausgewerteten Wert im Zielbereich festzulegen. Versuchen Sie esscope="request"
stattdessen, wodurch der Wert sofort ausgewertet wird (tatsächlich während der Erstellungszeit der Ansicht) und als Anforderungsattribut festgelegt wird (das während der Iteration nicht "überschrieben" wird). Unter der Decke wird einValueExpression
Objekt erstellt und festgelegt .ClassNotFoundException
. Die Laufzeitabhängigkeiten Ihres Projekts sind fehlerhaft. Höchstwahrscheinlich verwenden Sie einen Nicht-JavaEE-Server wie Tomcat und haben vergessen, JSTL zu installieren, oder Sie haben versehentlich sowohl JSTL 1.0 als auch JSTL 1.1+ eingeschlossen. Denn in JSTL 1.0 ist das Paketjavax.servlet.jstl.core.*
und seit JSTL 1.1 ist dies gewordenjavax.servlet.jsp.jstl.core.*
. Hinweise zur Installation von JSTL finden Sie hier: stackoverflow.com/a/4928309verwenden
quelle
Für die schalterartige Ausgabe können Sie die Schalterfläche von PrimeFaces Extensions verwenden.
quelle