web-dev-qa-db-fra.com

JSTL dans JSF2 Facelets ... a du sens?

Je voudrais sortir un peu de code Facelets sous condition.

Pour cela, les balises JSTL semblent bien fonctionner:

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

Cependant, je ne suis pas sûr que ce soit une bonne pratique? Y a-t-il un autre moyen d'atteindre mon objectif?

158
Jan

Introduction

Les balises JSTL <c:xxx> Sont toutes les gestionnaires de balises et sont exécutées au moment de la création de la vue , tandis que JSF <h:xxx> Sont toutes composants de l'interface utilisateur et sont exécutées pendant le temps de rendu de la vue .

Notez que depuis les propres balises <f:xxx> Et <ui:xxx> De JSF, seules celles qui font pas s'étendent de UIComponent sont également manipulateurs d'étiquettes, par exemple <f:validator>, <ui:include>, <ui:define>, Etc. Ceux qui s’étendent de UIComponent sont également des composants de l’interface utilisateur JSF, par ex. <f:param>, <ui:fragment>, <ui:repeat>, Etc. Depuis les composants de l'interface utilisateur JSF, seuls les attributs id et binding sont également évalués lors de la création 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.

La génération de la vue correspond à ce moment où le fichier XHTML/JSP doit être analysé et converti en une arborescence de composants JSF qui est ensuite stockée sous le nom UIViewRoot de FacesContext. Le temps de rendu de la vue correspond à l'instant où l'arborescence 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 balises JSTL ne s'exécutent pas de manière synchrone, contrairement à ce que l'on attend du codage. Vous pouvez le visualiser comme suit: JSTL s'exécute de haut en bas en produisant l'arborescence des composants JSF, puis c'est au tour de JSF de s'exécuter de haut en bas en produisant la sortie HTML.

<c:forEach> Vs <ui:repeat>

Par exemple, ce balisage Facelets itérant plus de 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 composants <h:outputText> distincts dans l'arborescence des composants JSF, représentés approximativement comme suit:

<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 génèrent individuellement leur sortie HTML pendant le 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 du caractère unique des ID de composant et que ceux-ci sont également évalués lors de la création de la vue.

Alors que ce balisage Facelets itère plus de 3 éléments en utilisant <ui:repeat>, Qui est un composant de l'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 composant <h:outputText> étant utilisé pendant le rendu de la vue pour générer une sortie HTML basée sur le tour 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 composant <ui:repeat>, En tant que composant NamingContainer, garantissait déjà l'unicité de l'ID client en fonction de l'index d'itération; Il est également impossible d'utiliser EL dans l'attribut id des composants enfants de cette manière, car il est également évalué pendant la construction de la vue, alors que #{item} n'est disponible que pendant le rendu de la vue. Même chose pour un h:dataTable Et des composants similaires.

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

Comme autre exemple, ce balisage Facelets ajoutant 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>

... dans le cas de type = TEXT ajoutera seulement le composant <h:inputText> à 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 se retrouver dans 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 field ne change jamais pendant au moins la portée de la vue). Vous pouvez également rencontrer EL trouble lorsque vous utilisez des sous-classes avec des propriétés supplémentaires dans les versions de Mojarra antérieures à la version 2.2.7.

<c:set> Vs <ui:param>

Ils ne sont pas interchangeables. Le paramètre <c:set> Définit une variable dans l'étendue EL, qui n'est accessible que après l'emplacement de la balise pendant la construction de la vue, mais n'importe où dans la vue pendant le rendu de la vue. Le <ui:param> Transmet une variable EL à un modèle Facelet inclus via <ui:include>, <ui:decorate template> Ou <ui:composition template>. Les anciennes versions de JSF comportaient des bogues, la variable <ui:param> Étant également disponible en dehors du modèle Facelet en question, il ne faut jamais s'y fier.

Le <c:set> Sans un attribut scope se comportera comme un alias. Il ne met pas en cache le résultat de l'expression EL dans aucune étendue. Il peut donc parfaitement être utilisé, par exemple, en itérant des 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>

Cela ne convient pas pour, par exemple, calculer la somme dans une boucle. Pour cela, utilisez plutôt flux EL 3. :

<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 l'une des valeurs autorisées: request, view, session ou application, alors être évalué immédiatement pendant la 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" />

Ceci ne sera évalué qu'une fois et sera disponible sous la forme #{dev} Dans toute la candidature.

Utiliser JSTL pour contrôler la construction de l'arborescence des composants JSF

L'utilisation de JSTL peut uniquement produire des résultats inattendus lorsqu'elle est utilisée dans des composants itératifs JSF tels que <h:dataTable>, <ui:repeat>, Etc., ou lorsque les attributs de balise JSTL dépendent des résultats d'événements JSF tels que preRenderView ou les valeurs de formulaire soumises dans le modèle qui ne sont pas disponibles lors de la génération de la vue. Utilisez donc les balises JSTL uniquement pour contrôler le flux de construction 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 le var de l'itération de composants JSF aux attributs de balise JSTL. Ne comptez pas sur les événements JSF dans les attributs de balise JSTL.

Chaque fois que vous pensez que vous avez besoin de lier un composant au bean de support via binding, ou d’en prendre un via findComponent(), et de créer/manipuler ses enfants à l’aide de Java code dans un bean de support avec new SomeComponent() et tout le reste, vous devez immédiatement arrêter et envisager d'utiliser JSTL à la place. JSTL étant également basé sur XML, le code nécessaire à la création dynamique de composants JSF deviendra beaucoup plus lisible et maintenable.

Il est important de savoir que les versions de Mojarra antérieures à la version 2.1.18 présentaient un bogue lors de la sauvegarde partielle de l’état lorsqu’il faisait référence à un bean avec une portée de vue dans un attribut de balise JSTL. Le bean complet de la vue serait recréé récemment au lieu d'être extrait de l'arborescence de la vue (tout simplement parce que l'arborescence de la vue complète n'est pas encore disponible au point JSTL fonctionne). Si vous attendez ou stockez un état dans le bean délimité par la vue par un attribut de balise JSTL, il ne renverra pas la valeur attendue, ou il sera "perdu" dans le bean réel délimité par la vue qui est restauré après la vue. l'arbre est construit. Si vous ne pouvez pas effectuer la mise à niveau vers Mojarra 2.1.18 ou une version ultérieure, vous devez désactiver l’épargne partielle dans l’état dans 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 dans lesquels les balises JSTL sont utiles (c'est-à-dire lorsqu'elles sont réellement utilisées correctement lors de la création de la vue), consultez les questions/réponses suivantes:


En un mot

En ce qui concerne vos exigences fonctionnelles concrètes, si vous souhaitez restituer des composants JSF de manière conditionnelle, utilisez plutôt l'attribut rendered du composant HTML JSF, particulièrement 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 voulez construire (créer/ajouter) des composants JSF de manière conditionnelle, continuez à utiliser JSTL. C'est bien mieux que de faire verbalement new SomeComponent() en Java.

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

Voir également:

308
BalusC

utilisation

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

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

Pour une sortie de type switch, vous pouvez utiliser switch from primefaces-extensions .

4
Ravshan Samandarov