Supposons que l'on souhaite afficher un formulaire de saisie (ou tout autre composant) dynamiquement dans une page (affichant d'autres choses) au moment ou l'utilisateur clique sur un bouton
A compléter
Schéma ou exemple
Piste 1 : utiliser le rendered
... celui du form ou d'un panelGrid avec un booléen dans le contrôleur (ici showUserForm)
<h:panelGrid id="panelFormAddUser" columns="1" rendered="#{welcomeController.showUserForm}">
<h:form id="formAddUser">
[...]
</h:form>
</h:panelGrid>
...activé par le bouton suivant
<p:commandButton value="Afficher formulaire" update="@form,panelFormAddUser,growl"
action="#{welcomeController.showFormNewUser}" >
</p:commandButton>
... qui donne, si true
<table id="panelFormAddUser"><tbody>
<tr><td>
<form id="formAddUser" name="formAddUser" method="post" action="/stylesheets/private/todo_private_demo.xhtml" enctype="application/x-www-form-urlencoded">
[...]
</form></td></tr>
</tbody>
</table>
...si false
rien du tout :-)
donc update="...panelFormAddUser..." ajax n'a aucun effet puisqu'il n'est pas sur la page.
En revanche si on désactive ajax sur le bouton la page est rechargée complètement :
<p:commandButton value="Afficher formulaire" actionListener="#{welcomeController.showFormNewUser}" ajax="false">
</p:commandButton>
... ça marche
Il faut donc un contenant qui s'affiche toujours, qui sera vide par défaut et contiendra le formulaire à l'appel du clic
Piste 2 : Utiliser un binding
Ceci consiste à mapper un objet qui compose la vue à une propriété du contrôleur.
On déclare par exemple dans welcomeController :
import javax.faces.component.UIComponent;
[...]
private UIComponent panelUserForm;
[...]
public void showFormNewUser() {
this.panelUserForm.setRendered(true);
}
public UIComponent getPanelUserForm() {
return panelUserForm;
}
public void setPanelUserForm(UIComponent panelUserForm) {
this.panelUserForm = panelUserForm;
}
[...]
... et dans la JSP on mappe :
<h:panelGrid id="panelFormAddUser" binding="#{welcomeController.panelUserForm}" columns="1" rendered="false">
<h:form id="formAddUser">
[...]
</h:form>
</h:panelGrid>
Comme précédemment, update="...panelFormAddUser..." ajax n'a aucun effet puisqu'il n'est pas sur la page.
En revanche si on désactive ajax sur le bouton la page est rechargée complètement :
<p:commandButton value="Afficher formulaire" actionListener="#{welcomeController.showFormNewUser}" ajax="false">
</p:commandButton>
... ça marche
Il faut donc un contenant qui s'affiche toujours, qui sera vide par défaut et contiendra le formulaire à l'appel du clic
Piste 3 : Un contenant vide qui sera mis à jour au moment du clic
Pour que seul le panel soit rafraichi avec Ajax e non la page complète nous allons repartir de l'exemple précédent et laisser le panelGrid toujours visible et on va jouer sur la visibilité du formulaire.
<h:panelGrid id="panelFormAddUser" binding="#{welcomeController.panelUserForm}" columns="1" rendered="true">
<h:form id="formAddUser" rendered="false">
[...]
</h:form>
</h:panelGrid>
avec le bouton
<p:commandButton value="Afficher formulaire" update="@form,panelFormAddUser,growl"
action="#{welcomeController.showFormNewUser}" >
</p:commandButton>
Désormais au clic on appellera :
public void showFormNewUser() {
this.panelUserForm.getChildren().get(0).setRendered(true);
}
Là ça marche, au clic le rendered du formulaire est positionné à true et le panel est rafraichi.
Pour améliorer
On va maintenant ajouter un bouton dans le formulaire pour cacher ce dernier.
On a donc l'exemple complet :
<p:commandButton value="Afficher formulaire" update="@form,panelFormAddUser,growl"
action="#{welcomeController.showFormNewUser}" >
</p:commandButton>
[...]
<h:panelGrid id="panelFormAddUser" binding="#{welcomeController.panelUserForm}" columns="1" rendered="true">
<h:form id="formAddUser" rendered="false">
[...]
</h:form>
<h:form id="formhideAddUser" rendered="false">
<p:commandButton value="Masquer formulaire" update="panelFormAddUser,growl"
action="#{welcomeController.hideFormNewUser}" >
</p:commandButton>
</h:form>
</h:panelGrid>
Désormais aux clics on appellera le cas échéant :
import javax.faces.component.UIComponent;
[...]
private UIComponent panelUserForm;
public void showFormNewUser() {
for (UIComponent component : this.panelUserForm.getChildren()) {
component.setRendered(true);
}
}
public void hideFormNewUser() {
for (UIComponent component : this.panelUserForm.getChildren()) {
component.setRendered(false);
}
}
Pour améliorer encore un peu
On intègre le bouton d'affichage du formulaire dans le panel et on switch les rendered
<h:panelGrid id="panelFormAddUser" binding="#{welcomeController.panelUserForm}" columns="1" rendered="true">
<h:form id="formshowAddUser" rendered="true">
<p:commandButton value="Afficher formulaire" update="@form,panelFormAddUser,growl"
action="#{welcomeController.toogleFormNewUser}" >
</p:commandButton>
</h:form>
<h:form id="formAddUser" rendered="false">
[...]
</h:form>
<h:form id="formhideAddUser" rendered="false">
<p:commandButton value="Masquer formulaire" update="panelFormAddUser,growl"
action="#{welcomeController.toogleFormNewUser}" >
</p:commandButton>
</h:form>
</h:panelGrid>
Désormais aux clics on appellera une seule méthode :
import javax.faces.component.UIComponent;
[...]
private UIComponent panelUserForm;
public void toogleFormNewUser() {
for (UIComponent component : this.panelUserForm.getChildren()) {
if (component.getAttributes().get("rendered").toString().equals("true"))
component.setRendered(false);
else
component.setRendered(true);
}
}
Piste 4 : La solution de facilité... un composant
Primefaces propose par exemple le composant dialog
<p:commandButton value="Afficher formulaire" onclick="dialogNewUser.show();" type="button"/>
[...]
<p:dialog header="Création d'un utilisateur" widgetVar="dialogNewUser" modal="true">
<h:form id="formAddUser">
[...]
</h:form>
</p:dialog>