JSTL in JSF2 Facelets ... ça a du sens?

j'aimerais sortir un peu de code Facelets conditionnellement.

pour cette raison, les étiquettes JSTL semblent fonctionner très bien:

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

cependant, je ne suis pas sûr que ce soit une bonne pratique? Est-il une autre voie pour atteindre mon objectif?

149
demandé sur BalusC 2010-07-27 15:11:36

3 réponses

Introduction

JSTL <c:xxx> balises sont tous des taghandlers et ils sont exécutés au cours de afficher le temps de construction , tandis que le JSF <h:xxx> balises sont tous des composants de l'INTERFACE utilisateur et ils sont exécutés au cours de afficher les temps de rendu .

noter que de JSF propres <f:xxx> et <ui:xxx> tags seulement ceux qui font Non s'étendent de UIComponent sont également des étiqueteuses, p.ex. <f:validator> , <ui:include> , <ui:define> , etc. Ceux qui s'étendent de UIComponent sont aussi des composants D'UI JSF, par exemple <f:param> , <ui:fragment> , <ui:repeat> , etc. À partir des composants D'UI de JSF, seuls les attributs id et binding sont également évalués pendant le temps de construction de la vue. Ainsi, la réponse ci-dessous concernant le cycle de vie de JSTL s'applique également aux id et binding ." caractéristiques des composantes du JSF.

le temps de construction de la vue est le moment où le fichier XHTML/JSP doit être analysé et converti en un arbre de composants JSF qui est ensuite stocké comme UIViewRoot du FacesContext . Le temps de rendu de la vue est le moment où L'arbre des composants JSF est sur le point de générer du HTML, en commençant par UIViewRoot#encodeAll() . Ainsi: les composants de L'interface utilisateur JSF et les étiquettes JSTL ne fonctionnent pas de manière synchronisée comme vous pouvez vous y attendre du fait du codage. Vous pouvez le visualiser comme suit: JSTL court de haut en en bas tout d'abord, produire l'arborescence des composants JSF, puis c'est au tour de JSF de courir de haut en bas à nouveau, produire la sortie HTML.

<c:forEach> vs <ui:repeat>

par exemple, ce marqueur Facelets itérant plus de 3 articles 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 le temps de construction de la vue trois composants séparés <h:outputText> dans l'arbre des composants JSF, représenté grossièrement 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 manuellement assurer de l'unicité des Id composant et que ceux-ci sont également évalués pendant les temps de construction.

alors que cette marge de Facelets itérant plus de 3 articles en utilisant <ui:repeat> , qui est une composante D'UI JSF:

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

...se retrouve déjà dans le JSF arbre des composants dans lequel le même composant <h:outputText> est pendant le rendu de vue temps étant réutilisé pour générer la 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 composant NamingContainer assurait déjà le caractère unique de l'ID du client basé sur l'indice d'itération; il n'est pas non plus possible d'utiliser EL dans l'attribut id des composants enfants de cette façon car il est également évalué en vue le temps de compilation alors que #{item} n'est disponible que pendant le temps de rendu de la vue. Il en va de même pour un h:dataTable et des composants similaires.

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

comme autre exemple, CE Marquage Facelets ajoutant conditionnellement différentes étiquettes 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>

...dans le cas de type = TEXT ajouter seulement le composant <h:inputText> à l'arbre des composants JSF:

<h:inputText ... />

alors que cette marque de Facelets:

<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />

...se retrouvera exactement comme ci-dessus dans l'arbre des composants JSF quelle que soit la condition. Cela peut donc se retrouver dans un arbre de composants" gonflé "quand vous en avez beaucoup et qu'ils sont en fait basés sur un modèle" statique "(i.e. le field ne change jamais pendant au moins la portée de la vue). Aussi, vous pouvez rencontrer EL trouble lorsque vous traitez de sous-classes avec des propriétés supplémentaires dans les versions Mojarra avant 2.2.7.

<c:set> vs <ui:param>

Ils ne sont pas interchangeables. Le <c:set> définit une variable dans la portée EL, qui est accessible seulement après l'emplacement de la balise pendant le temps de construction de la vue, mais n'importe où dans la vue pendant le temps de rendu de la vue. Le <ui:param> passe une variable EL à un modèle Facelet inclus via <ui:include> , <ui:decorate template> , ou <ui:composition template> . Les versions plus anciennes du JSF présentaient des bugs pour lesquels la variable <ui:param> est également disponible en dehors du modèle Facelet en question, il ne faut jamais s'y fier.

le <c:set> sans attribut scope se comportera comme un alias. Il ne cache pas le résultat de L'expression EL dans aucun scope. Il peut ainsi parfaitement bien être utilisé à l'intérieur par exemple des composants JSF itératifs. 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 seulement pour par exemple calculer la somme dans une boucle. Pour cela, utilisez plutôt 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>

seulement, lorsque vous définissez l'attribut scope avec une des valeurs autorisées request , view , session , ou application , alors il sera évalué immédiatement pendant le temps de construction de la vue et stocké dans la portée spécifiée.

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

ce document ne sera évalué qu'une seule fois et sera disponible en tant que #{dev} dans toute la demande.

utiliser JSTL pour contrôler JSF component tree building

utilisant JSTL ne peut conduire à des résultats inattendus que lorsque l'on utilise à l'intérieur de composants itératifs JSF tels que <h:dataTable> , <ui:repeat> , etc, ou lorsque les attributs de la balise JSTL dépendent des résultats d'événements JSF tels que preRenderView ou des valeurs de forme soumises dans le modèle qui ne sont pas disponible pendant les temps de construction. Ainsi, utilisez les étiquettes JSTL uniquement pour contrôler le flux de la construction de l'arbre des composants JSF. Utilisez les composants JSF UI pour contrôler le flux de génération de sortie HTML. Ne liez pas les var des composants JSF itératifs aux attributs de la balise JSTL. Ne vous fiez pas aux événements JSF dans les attributs de la balise JSTL.

chaque fois que vous pensez avoir besoin de lier un composant au support via binding , ou d'en saisir un via findComponent() , et de créer/manipuler ses enfants en utilisant Java code dans une fève de soutien avec new SomeComponent() et ce qui ne l'est pas, alors vous devriez immédiatement arrêter et envisager D'utiliser JSTL à la place. Comme JSTL est aussi basé sur XML, le code nécessaire pour créer dynamiquement les composants JSF deviendra tellement plus lisible et maintenable.

Important à savoir est que les versions de Mojarra plus anciennes que 2.1.18 avaient un bug dans la sauvegarde partielle de l'état quand faisant référence à une vue scoped bean dans un attribut d'étiquette JSTL. La totalité de la vue scoped bean serait nouvellement recréé au lieu de récupéré de l'arbre de vue (simplement parce que l'arbre de vue complète n'est pas encore disponible au point JSTL fonctionne). Si vous attendez ou stockez un État dans la vue scoped bean par un attribut d'étiquette JSTL, alors il ne retournera pas la valeur que vous attendez, ou il sera "perdu" dans la vraie vue scoped bean qui est restauré après que l'arbre de vue est construit. Dans le cas où vous ne pouvez pas passer à Mojarra 2.1.18 ou plus récent, le travail autour est de désactiver l'épargne partielle de l'état web.xml comme ci-dessous:

<context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>false</param-value>
</context-param>

voir aussi:

pour voir quelques exemples du monde réel où les étiquettes JSTL sont utiles (c.-à-d. quand vraiment utilisé correctement pendant la construction de la vue), voir les questions/réponses suivantes:


en bref

en ce qui concerne vos exigences fonctionnelles concrètes, si vous voulez rendre composants JSF conditionnellement, utilisez l'attribut rendered sur le composant HTML JSF, en particulier si #{lpc} représente l'item 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 voulez construire (créer/ajouter) composants JSF conditionnellement, puis continuer à utiliser JSTL. C'est beaucoup mieux que de faire verbeusement new SomeComponent() en java.

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

voir aussi:

287
répondu BalusC 2018-03-13 17:15:55

utiliser

<h:panelGroup rendered="#{lpc.verbose}">
  ...
</h:panelGroup>
12
répondu Bozho 2015-05-27 07:47:03

désolé pour la réponse séparée, mais je ne pouvais pas commenter les réponses ci-dessus.

pour la sortie de type commutateur, vous pouvez utiliser le commutateur de primefaces-extensions .

4
répondu Ravshan Samandarov 2016-08-25 09:58:47