webentwicklung-frage-antwort-db.com.de

JSTL in JSF2 Facelets ... macht Sinn?

Ich möchte ein bisschen Facelets-Code bedingt ausgeben.

Zu diesem Zweck scheinen die JSTL-Tags gut zu funktionieren:

<c:if test="${lpc.verbose}">
    ...
</c:if>

Ich bin mir jedoch nicht sicher, ob dies eine bewährte Methode ist. Gibt es einen anderen Weg, um mein Ziel zu erreichen?

158
Jan

Einführung

JSTL-Tags <c:xxx> Sind alle Taghandler und werden während der Erstellungszeitviewausgeführt, während JSF-Tags <h:xxx> Alle I) sind Komponenten und sie werden währendview render timeausgeführt.

Beachten Sie, dass von den JSF-eigenen Tags <f:xxx> Und <ui:xxx> Nur diejenigen Tag-Handler sind, die not extend from UIComponent ausführen, z. <f:validator>, <ui:include>, <ui:define> Usw. Diejenigen, die sich von UIComponent erstrecken, sind auch JSF-UI-Komponenten, z. <f:param>, <ui:fragment>, <ui:repeat> Usw. In JSF-UI-Komponenten werden nur die Attribute id und binding während der Erstellungszeit der Ansicht ausgewertet . Die folgende Antwort zum JSTL-Lebenszyklus gilt daher auch für die Attribute id und binding von JSF-Komponenten.

Die View-Build-Zeit ist der Moment, in dem die XHTML/JSP-Datei analysiert und in einen JSF-Komponentenbaum konvertiert werden soll, der dann als UIViewRoot des FacesContext gespeichert wird. Die Renderzeit der Ansicht ist der Moment, in dem der JSF-Komponentenbaum HTML generieren soll, 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 sich das folgendermaßen vorstellen: JSTL wird zuerst von oben nach unten ausgeführt und erzeugt den JSF-Komponentenbaum. Anschließend wird JSF erneut von oben nach unten ausgeführt und erzeugt die HTML-Ausgabe.

<c:forEach> Vs <ui:repeat>

Beispiel: Dieses Facelets-Markup durchläuft 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 ihrerseits ihre HTML-Ausgabe während der Renderzeit der Ansicht einzeln 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 mit <ui:repeat>, Einer JSF-UI-Komponente, über 3 Elemente iteriert:

<ui:repeat id="items" value="#{bean.items}" var="item">
    <h:outputText id="item" value="#{item.value}" />
</ui:repeat>

... landet bereits im JSF-Komponentenbaum, wobei die gleiche <h:outputText> - Komponente während des Renderns der Ansichtwiederverwendetwird, um basierend auf der aktuellen Iterationsrunde eine HTML-Ausgabe 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 die Komponente <ui:repeat> Als NamingContainer bereits die Eindeutigkeit der Client-ID basierend auf dem Iterationsindex sichergestellt hat. Auf diese Weise ist es auch nicht möglich, EL im id -Attribut untergeordneter Komponenten zu verwenden, da es auch während der Erstellungszeit der Ansicht ausgewertet wird, während #{item} nur während der Renderzeit der Ansicht verfügbar ist. Gleiches gilt für einen h:dataTable Und ähnliche Komponenten.

<c:if>/<c:choose> Vs rendered

Als ein weiteres Beispiel fügt dieses Facelets-Markup mit <c:if> Bedingt verschiedene Tags hinzu (Sie können auch <c:choose><c:when><c:otherwise> Verwenden):

<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>

... fügt bei type = TEXT nur die Komponente <h:inputText> 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 in einem "aufgeblähten" Komponentenbaum enden, wenn Sie viele davon haben und diese tatsächlich auf einem "statischen" Modell basieren (d. H., Das field ändert sich zumindest während des Ansichtsbereichs nie). Außerdem kann es vorkommen, dass Sie auf EL trouble stoßen, wenn Sie mit Unterklassen mit zusätzlichen Eigenschaften in Mojarra-Versionen vor 2.2.7 arbeiten.

<c:set> Vs <ui:param>

Sie sind nicht austauschbar. Mit <c:set> Wird eine Variable im EL-Bereich festgelegt, auf die nur nach der Tag-Position während der Ansichtserstellungszeit zugegriffen werden kann, während der Ansichtsrenderzeit jedoch an einer beliebigen Stelle in der Ansicht. Der <ui:param> Übergibt eine EL-Variable an eine Facelet-Vorlage, die über <ui:include>, <ui:decorate template> Oder <ui:composition template> Enthalten ist. Ältere JSF-Versionen hatten Fehler, bei denen die Variable <ui:param> Auch außerhalb der betreffenden Facelet-Vorlage verfügbar war. Darauf sollte man sich nie verlassen.

Das Attribut <c:set> Ohne scope verhält sich wie ein Alias. Das Ergebnis des EL-Ausdrucks wird in keinem Bereich zwischengespeichert. Es kann also sehr gut in beispielsweise iterierenden JSF-Komponenten verwendet werden. So kann z.B. unten wird 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 für z.B. Berechnung der Summe in einer Schleife. Verwenden Sie stattdessen 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 scope -Attribut auf einen der zulässigen Werte request, view, session oder application setzen, wird dies der Fall sein sofort während der Erstellungszeit der Ansicht ausgewertet und im angegebenen Bereich gespeichert werden.

<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />

Dies wird nur einmal ausgewertet und steht in der gesamten Anwendung als #{dev} Zur Verfügung.

Verwenden Sie JSTL, um die Erstellung des JSF-Komponentenbaums zu steuern

Die Verwendung von JSTL kann nur zu unerwarteten Ergebnissen führen, wenn JSF-Iterationskomponenten wie <h:dataTable>, <ui:repeat> Usw. verwendet werden oder wenn JSTL-Tag-Attribute von Ergebnissen von JSF-Ereignissen wie preRenderView oder übermittelte Formularwerte im Modell, die während der Erstellungszeit der Ansicht nicht verfügbar sind. Verwenden Sie daher nur JSTL-Tags, um den Ablauf der Erstellung des JSF-Komponentenbaums zu steuern. Verwenden Sie JSF-UI-Komponenten, um den Ablauf der HTML-Ausgabegenerierung zu steuern. Binden Sie die var der iterierenden JSF-Komponenten nicht an JSTL-Tag-Attribute. Verlassen Sie sich nicht auf JSF-Ereignisse in JSTL-Tag-Attributen.

Wann immer Sie denken, dass Sie eine Komponente über binding an die Backing-Bean binden oder über findComponent() eine Komponente abrufen und ihre untergeordneten Elemente mit Java erstellen/bearbeiten müssen Code in einer Backing-Bean mit new SomeComponent() und was nicht, dann sollten Sie sofort aufhören und stattdessen JSTL verwenden. Da JSTL auch XML-basiert ist, wird der Code, der zum dynamischen Erstellen von JSF-Komponenten benötigt wird, viel besser lesbar und lesbarer wartbar.

Wichtig zu wissen ist, dass in Mojarra-Versionen älter als 2.1.18 ein Fehler beim Speichern des Teilstatus aufgetreten ist, wenn auf eine View-Scoped-Bean in einem JSTL-Tag-Attribut verwiesen wurde. Die Bean für den gesamten View-Bereich würdenewerneut erstellt, anstatt aus dem View-Baum abgerufen zu werden (einfach, weil der vollständige View-Baum zum Zeitpunkt der Ausführung von JSTL noch nicht verfügbar ist). Wenn Sie einen Status in der Bean mit Sichtbereichsangabe durch ein JSTL-Tag-Attribut erwarten oder speichern, wird der erwartete Wert nicht zurückgegeben, oder er geht in der Bean mit Sichtbereichsangabe "verloren", die nach der Sicht 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 von Teilzuständen in 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 (d. H., Wenn sie beim Erstellen der Ansicht wirklich richtig verwendet werden):


In einer Nussschale

Wenn Sie Ihre konkrete FunktionsanforderungrenderJSF-Komponenten bedingt ausführen möchten, verwenden Sie stattdessen das rendered -Attribut für die JSF-HTML-Komponente, insbesondere if #{lpc} Repräsentiert das aktuell iterierte Element einer JSF-iterierenden Komponente wie <h:dataTable> Oder <ui:repeat>.

<h:someComponent rendered="#{lpc.verbose}">
    ...
</h:someComponent>

Wenn Sie JSF-Komponentenbuild(create/add) bedingt erstellen möchten, verwenden Sie weiterhin JSTL. Es ist viel besser, als new SomeComponent() in Java wörtlich zu tun.

<c:if test="#{lpc.verbose}">
    <h:someComponent>
        ...
    </h:someComponent>
</c:if>

Siehe auch:

308
BalusC

verwenden

<h:panelGroup rendered="#{lpc.verbose}">
  ...
</h:panelGroup>
13
Bozho

Entschuldigung für die separate Antwort, aber ich konnte die Antworten oben nicht kommentieren.

Für eine switch-ähnliche Ausgabe können Sie switch from primefaces-extensions verwenden.

4