introduction
Les <c:xxx>
balises JSTL sont toutes des gestionnaires de balises et elles sont exécutées au moment de la création de la vue , tandis que les <h:xxx>
balises JSF sont toutes des composants de l'interface utilisateur et elles sont exécutées au moment du rendu de la vue .
Notez que des propres JSF <f:xxx>
et <ui:xxx>
balises uniquement ceux qui ne pas prolonger de UIComponent
sont également taghandlers, par exemple <f:validator>
, <ui:include>
, <ui:define>
, etc. Ceux qui s'étendent UIComponent
sont également des composants JSF interface utilisateur, par exemple <f:param>
, <ui:fragment>
, <ui:repeat>
, etc. A partir de composants JSF UI seulement les id
et binding
attributs sont également évalué pendant la génération de la vue. Ainsi, la réponse ci-dessous concernant le cycle de vie JSTL s'applique également aux attributs id
et binding
des composants JSF.
L'heure de création de la vue correspond au moment où le fichier XHTML / JSP doit être analysé et converti en une arborescence de composants JSF qui est ensuite stockée à partir UIViewRoot
du FacesContext
. Le temps de rendu de la vue correspond au moment où l'arborescence des composants JSF est sur le point de générer du HTML, en commençant par UIViewRoot#encodeAll()
. Donc: les composants de l'interface utilisateur JSF et les balises JSTL ne fonctionnent pas de manière synchronisée comme vous l'attendriez du codage. Vous pouvez le visualiser comme suit: JSTL s'exécute d'abord de haut en bas, produisant l'arborescence des composants JSF, puis c'est au tour de JSF de s'exécuter de haut en bas à nouveau, produisant la sortie HTML.
<c:forEach>
contre <ui:repeat>
Par exemple, ce balisage Facelets itère sur 3 éléments en utilisant <c:forEach>
:
<c:forEach items="#{bean.items}" var="item">
<h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>
... crée pendant la construction de la vue trois <h:outputText>
composants distincts dans l'arborescence des composants JSF, représentés à peu près comme ceci:
<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}" />
... qui à leur tour génèrent individuellement leur sortie HTML pendant le temps de rendu de la vue:
<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>
Notez que vous devez vous assurer manuellement de l'unicité des ID de composant et que ceux-ci sont également évalués lors de la génération de la vue.
Alors que ce balisage Facelets itère sur 3 éléments en utilisant <ui:repeat>
, qui est un composant d'interface utilisateur JSF:
<ui:repeat id="items" value="#{bean.items}" var="item">
<h:outputText id="item" value="#{item.value}" />
</ui:repeat>
... se termine déjà tel quel dans l'arborescence des composants JSF, le même <h:outputText>
composant étant réutilisé pendant le temps de rendu de la vue pour générer une sortie HTML basée sur le cycle d'itération actuel:
<span id="items:0:item">value1</span>
<span id="items:1:item">value2</span>
<span id="items:2:item">value3</span>
Notez que le <ui:repeat>
comme étant un NamingContainer
composant garantissait déjà l'unicité de l'ID client basé sur l'index d'itération; il n'est pas non plus possible d'utiliser EL dans l' id
attribut des composants enfants de cette façon car il est également évalué pendant la construction de la vue alors qu'il #{item}
n'est disponible que pendant le temps de rendu de la vue. La même chose est vraie pour un h:dataTable
et des composants similaires.
<c:if>
/ <c:choose>
vsrendered
Comme autre exemple, ce balisage Facelets ajoute conditionnellement différentes balises en utilisant <c:if>
(vous pouvez également utiliser <c:choose><c:when><c:otherwise>
pour cela):
<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
ajoutera uniquement le <h:inputText>
composant à l'arborescence des composants JSF:
<h:inputText ... />
Alors que ce balisage Facelets:
<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />
... se terminera exactement comme ci-dessus dans l'arborescence des composants JSF, quelle que soit la condition. Cela peut donc aboutir à une arborescence de composants "gonflée" lorsque vous en avez beaucoup et qu'ils sont en fait basés sur un modèle "statique" (c'est-à-dire que le field
ne change jamais pendant au moins la portée de la vue). De plus, vous pouvez rencontrer des problèmes EL lorsque vous traitez avec des sous-classes avec des propriétés supplémentaires dans les versions Mojarra antérieures à la 2.2.7.
<c:set>
contre <ui:param>
Ils ne sont pas interchangeables. Les <c:set>
jeux d' une variable dans le champ EL, qui est accessible uniquement après l'emplacement de la balise durant la compilation de vue, mais partout dans la vue pendant la vue du rendu. La <ui:param>
passe d' une variable à un modèle EL Facelet inclus via <ui:include>
, <ui:decorate template>
ou <ui:composition template>
. Les anciennes versions de JSF avaient des bogues dans lesquels la <ui:param>
variable est également disponible en dehors du modèle Facelet en question, cela ne devrait jamais être invoqué.
Le <c:set>
sans scope
attribut se comportera comme un alias. Il ne met en cache le résultat de l'expression EL dans aucune portée. Il peut donc parfaitement être utilisé à l'intérieur, par exemple, de l'itération de composants JSF. Ainsi, par exemple ci-dessous fonctionnera bien:
<ui:repeat value="#{bean.products}" var="product">
<c:set var="price" value="#{product.price}" />
<h:outputText value="#{price}" />
</ui:repeat>
Il ne convient pas, par exemple, pour calculer la somme dans une boucle. Pour cela, utilisez plutôt le flux EL 3.0 :
<ui:repeat value="#{bean.products}" var="product">
...
</ui:repeat>
<p>Total price: #{bean.products.stream().map(product->product.price).sum()}</p>
Seulement, lorsque vous définissez l' scope
attribut avec l' une des valeurs autorisées request
, view
, session
ou application
, il sera évalué immédiatement au moment de la construction de la vue et stockée dans le champ spécifié.
<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />
Celui-ci ne sera évalué qu'une seule fois et disponible comme #{dev}
dans toute la candidature.
Utilisez JSTL pour contrôler la création de l'arborescence des composants JSF
L' utilisation JSTL ne peut conduire à des résultats inattendus lorsqu'ils sont utilisés à l' intérieur des composants JSF tels que <h:dataTable>
, <ui:repeat>
, etc, ou lorsque les attributs de balise JSTL dépendent des résultats des événements JSF tels que preRenderView
ou les valeurs de forme présentées dans le modèle qui ne sont pas disponibles pendant la vue du temps de construction . Par conséquent, utilisez les balises JSTL uniquement pour contrôler le flux de création de l'arborescence des composants JSF. Utilisez les composants de l'interface utilisateur JSF pour contrôler le flux de génération de sortie HTML. Ne liez pas les var
composants JSF en cours d'itération aux attributs de balise JSTL. Ne vous fiez pas aux événements JSF dans les attributs de balise JSTL.
Chaque fois que vous pensez avoir besoin de lier un composant au backing bean via binding
, ou d'en saisir un via findComponent()
, et de créer / manipuler ses enfants en utilisant du code Java dans un backing bean avec new SomeComponent()
et quoi pas, alors vous devez immédiatement arrêter et envisager d'utiliser JSTL à la place. Comme JSTL est également basé sur XML, le code nécessaire pour créer dynamiquement des composants JSF deviendra tellement mieux lisible et maintenable.
Il est important de savoir que les versions de Mojarra antérieures à 2.1.18 avaient un bogue lors de la sauvegarde d'état partielle lors du référencement d'un bean à portée de vue dans un attribut de balise JSTL. Le bean à portée de vue entière serait nouvellement recréé au lieu d'être extrait de l'arborescence de vues (simplement parce que l'arborescence de vues complète n'est pas encore disponible au moment où JSTL s'exécute). Si vous attendez ou stockez un état dans le bean à portée de vue par un attribut de balise JSTL, il ne retournera pas la valeur que vous attendez, ou il sera "perdu" dans le bean à portée de vue réelle qui est restauré après la vue l'arbre est construit. Dans le cas où vous ne pouvez pas mettre à niveau vers Mojarra 2.1.18 ou une version plus récente, la solution consiste à désactiver l'enregistrement d'état partiel web.xml
comme ci-dessous:
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>false</param-value>
</context-param>
Voir également:
Pour voir des exemples concrets où les balises JSTL sont utiles (c'est-à-dire lorsqu'elles sont vraiment correctement utilisées lors de la construction de la vue), consultez les questions / réponses suivantes:
En un mot
En ce qui concerne vos exigences fonctionnelles concrètes, si vous souhaitez rendre des composants JSF de manière conditionnelle, utilisez rendered
plutôt l' attribut sur le composant HTML JSF, en particulier si #{lpc}
représente l'élément actuellement itéré d'un composant itératif JSF tel que <h:dataTable>
ou <ui:repeat>
.
<h:someComponent rendered="#{lpc.verbose}">
...
</h:someComponent>
Ou, si vous souhaitez créer (créer / ajouter) des composants JSF de manière conditionnelle, continuez à utiliser JSTL. C'est bien mieux que de le faire verbeusement new SomeComponent()
en java.
<c:if test="#{lpc.verbose}">
<h:someComponent>
...
</h:someComponent>
</c:if>
Voir également:
<ui:repeat>
s'agit d'un gestionnaire de balises (à cause de cette ligne, " Notez que JSF est propre<f:xxx>
et<ui:xxx>
... ") juste comme<c:forEach>
et par conséquent, il est évalué au moment de la construction de la vue (encore une fois comme juste comme<c:forEach>
) . Si tel est le cas, il ne devrait pas y avoir de différence fonctionnelle visible entre<ui:repeat>
et<c:forEach>
? Je ne comprends pas exactement ce que signifie ce paragraphe :)<f:xxx>
et les<ui:xxx>
balises qui ne s'étendent pasUIComponent
sont également des gestionnaires de balises. " tente-t-elle pas d'impliquer que<ui:repeat>
c'est aussi un gestionnaire de balises car<ui:xxx>
inclut également<ui:repeat>
? Cela devrait alors signifier que<ui:repeat>
c'est l'un des composants<ui:xxx>
qui s'étendUIComponent
. Par conséquent, ce n'est pas un gestionnaire de balises. (Certains d'entre eux peuvent ne pas s'étendreUIComponent
. Par conséquent, ce sont des gestionnaires de balises) Vraiment?<c:set>
withoutscope
crée un alias de l'expression EL au lieu de définir la valeur évaluée dans la portée cible. Essayez à lascope="request"
place, qui évaluera immédiatement la valeur (pendant la construction de la vue en effet) et la définira comme attribut de requête (qui ne sera pas «écrasé» pendant l'itération). Sous les couvertures, il crée et pose unValueExpression
objet.ClassNotFoundException
. Les dépendances d'exécution de votre projet sont rompues. Vous utilisez très probablement un serveur non JavaEE tel que Tomcat et vous avez oublié d'installer JSTL, ou vous avez accidentellement inclus à la fois JSTL 1.0 et JSTL 1.1+. Parce que dans JSTL 1.0, le package estjavax.servlet.jstl.core.*
et depuis JSTL 1.1, il est devenujavax.servlet.jsp.jstl.core.*
. Des indices pour l'installation de JSTL peuvent être trouvés ici: stackoverflow.com/a/4928309utilisation
la source
Pour la sortie de commutation semblable, vous pouvez utiliser le commutateur visage PrimeFaces Extensions.
la source