web-dev-qa-db-fra.com

Comment ajax-refresh dynamic inclure du contenu par le menu de navigation? (JSF SPA)

J'apprends juste JSF 2 grâce à ce site que j'avais beaucoup appris en si peu de temps.

Ma question est de savoir comment implémenter une mise en page commune à toutes mes pages JSF 2 et que seule la partie de contenu de la page soit actualisée et non la page entière chaque fois que je clique sur un lien/menu à partir d'un panneau différent. J'utilise l'approche Facelets, elle fait ce que je veux, sauf que chaque fois que je clique sur un lien d'un panneau (par exemple, les éléments de menu du panneau de gauche), la page entière est actualisée. Ce que je recherche, c'est un moyen de rafraîchir uniquement la partie contenu de ma page. Pour illustrer cela ci-dessous, voici ma mise en page cible.

enter image description here N'a pas posté mon code car je ne sais pas si Facelets peut le faire. Existe-t-il une autre approche mieux adaptée à mes besoins que les Facelets?

34
royjavelosa

Une approche simple serait le point de vue suivant:

<h:panelGroup id="header" layout="block">
    <h1>Header</h1>
</h:panelGroup>
<h:panelGroup id="menu" layout="block">
    <h:form>
        <f:ajax render=":content">
            <ul>
                <li><h:commandLink value="include1" action="#{bean.setPage('include1')}" /></li>            
                <li><h:commandLink value="include2" action="#{bean.setPage('include2')}" /></li>            
                <li><h:commandLink value="include3" action="#{bean.setPage('include3')}" /></li>            
            </ul>
        </f:ajax>
    </h:form>
</h:panelGroup>
<h:panelGroup id="content" layout="block">
    <ui:include src="/WEB-INF/includes/#{bean.page}.xhtml" />
</h:panelGroup>

Avec ce haricot:

@ManagedBean
@ViewScoped
public class Bean implements Serializable {

     private String page;

     @PostConstruct
     public void init() {
         page = "include1"; //  Default include.
     }

     // +getter+setter.
 }

Dans cet exemple, les modèles d'inclusion réels sont include1.xhtml, include2.xhtml et include3.xhtml dans /WEB-INF/includes dossier (le dossier et l'emplacement sont entièrement libres de votre choix; les fichiers sont simplement placés dans /WEB-INF pour empêcher l'accès direct en devinant l'URL dans la barre d'adresse du navigateur).

Cette approche fonctionne dans toutes les versions de MyFaces, mais nécessite un minimum de Mojarra 2.3.0. Si vous utilisez une version de Mojarra antérieure à 2.3.0, tout cela échoue lorsque le <ui:include> la page contient à son tour un <h:form>. Toute publication échouera car il manque totalement l'état d'affichage. Vous pouvez résoudre ce problème en effectuant une mise à niveau vers Mojarra 2.3.0 au minimum ou avec un script trouvé dans cette réponse h: commandButton/h: commandLink ne fonctionne pas au premier clic, ne fonctionne qu'au deuxième clic , ou si vous utilise déjà la bibliothèque d'utilitaires JSF OmniFaces , utilisez son script FixViewState . Ou, si vous utilisez déjà PrimeFaces et utilisez exclusivement <p:xxx> ajax, alors il est déjà pris en compte de manière transparente.

En outre, assurez-vous que vous utilisez un minimum de Mojarra 2.1.18 car les anciennes versions échoueront à garder le bean à portée de vue en vie, ce qui entraînera une mauvaise inclusion lors de la publication. Si vous ne pouvez pas mettre à niveau, vous devez revenir à l'approche ci-dessous (relativement maladroite) de rendre la vue conditionnellement au lieu de la construire conditionnellement:

...
<h:panelGroup id="content" layout="block">
    <ui:fragment rendered="#{bean.page eq 'include1'}">
        <ui:include src="include1.xhtml" />
    </ui:fragment>
    <ui:fragment rendered="#{bean.page eq 'include2'}">
        <ui:include src="include2.xhtml" />
    </ui:fragment>
    <ui:fragment rendered="#{bean.page eq 'include3'}">
        <ui:include src="include3.xhtml" />
    </ui:fragment>
</h:panelGroup>

L'inconvénient est que la vue deviendrait relativement grande et que tous les beans gérés associés pourraient être initialisés inutilement même s'ils ne seraient pas utilisés conformément à la condition rendue.

Quant au positionnement des éléments, il s'agit simplement d'appliquer le bon CSS. Cela dépasse le cadre de JSF :) Au moins, <h:panelGroup layout="block"> rend un <div>, donc ça devrait être suffisant.

Enfin, cette approche SPA (Single Page Application) n'est pas compatible avec le référencement. Toutes les pages ne sont ni indexables par les robots de recherche ni marquables par les utilisateurs finaux, vous devrez peut-être jouer avec l'historique HTML5 dans le client et fournir une solution de secours côté serveur. De plus, dans le cas de pages avec des formulaires, la même instance de bean de portée de vue serait réutilisée sur toutes les pages, ce qui entraînerait un comportement de portée non intuitif lorsque vous revenez à une page précédemment visitée. Je suggérerais plutôt d'utiliser une approche de modèle, comme indiqué dans la deuxième partie de cette réponse: Comment inclure un autre XHTML dans XHTML à l'aide de facettes JSF 2.0? Voir aussi Comment naviguer dans JSF? Comment faire en sorte que l'URL reflète la page actuelle (et non la précédente) .

62
BalusC

Si vous ne souhaitez actualiser qu'une partie de la page, il n'y a que 2 façons de procéder (pour le Web en général, pas seulement JSF). Vous devez utiliser des cadres ou Ajax. JSF 2 prend en charge ajax nativement, consultez la balise f: ajax pour mettre à jour un seul composant sans recharger la page entière.

2
GBa