Skip to content

5. Introducción a la biblioteca de componentes PrimeFaces

5.1. El papel de PrimeFaces en una aplicación JSF

Volvamos a la arquitectura de una aplicación JSF tal y como la hemos estudiado al principio de este documento:

Las páginas JSF se creaban con tres bibliotecas de etiquetas:

1
2
3
4
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  • línea 2: las etiquetas <h:x> del espacio de nombres [http://java.sun.com/jsf/html], que se corresponden con las etiquetas HTML,
  • línea 3: las etiquetas <f:y> del espacio de nombres [http://java.sun.com/jsf/core], que se corresponden con las etiquetas JSF,
  • línea 4: las etiquetas <ui:z> del espacio de nombres [http://java.sun.com/jsf/facelets], que se corresponden con las etiquetas de los facelets.

Para crear las páginas JSF, vamos a añadir una cuarta biblioteca de etiquetas: las de los componentes PrimeFaces.

1
2
3
<html xmlns="http://www.w3.org/1999/xhtml"
      ...
xmlns:p="http://primefaces.org/ui">
  • línea 3: las etiquetas <p:z> del espacio de nombres [http://primefaces.org/ui] corresponden a los componentes de PrimeFaces.

Esta es la única modificación que se producirá. Por lo tanto, aparecerá en las vistas. Los gestores de eventos y los modelos siguen siendo los mismos que con JSF. Es importante comprender este punto.

El uso de los componentes de PrimeFaces permite crear interfaces web más intuitivas gracias a los numerosos componentes de esta biblioteca y más fluidas gracias a la tecnología AJAX que utiliza de forma nativa. En este caso, hablamos de interfaces enriquecidas o RIA (Rich Internet Application).

La arquitectura JSF anterior se convertirá en la siguiente arquitectura PF (PrimeFaces):

5.2. Las aportaciones de Primefaces

La página web de Primefaces [http://www.primefaces.org/showcase/ui/home.jsf] ofrece una lista de los componentes que se pueden utilizar en una página PF:

En los ejemplos que vendrán a continuación, utilizaremos las dos primeras características de Primefaces:

  • algunos de los cientos de componentes que ofrece,
  • el comportamiento nativo de estos.

Entre los componentes disponibles:

En nuestros ejemplos solo utilizaremos unos quince de ellos, pero será suficiente para comprender los principios de construcción de una página de Primefaces.

5.3. Aprender a utilizar Primefaces

Primefaces ofrece ejemplos de uso de cada uno de sus componentes. Basta con hacer clic en el enlace correspondiente. Veamos un ejemplo:

  • en [1], el ejemplo del componente [Spinner],
  • en [2], el cuadro de diálogo que aparece tras hacer clic en el botón [Submit].

Aquí encontramos tres novedades:

  • el componente [Spinner], que no existe de forma predeterminada en JSF,
  • lo mismo ocurre con el cuadro de diálogo,
  • y, por último, el POST, provocado por el [Submit], está implementado con AJAX. Si observamos atentamente el navegador durante el POST, no vemos el reloj de arena. La página no se recarga. Simplemente se modifica: aparece en ella un nuevo componente, en este caso el cuadro de diálogo.

Veamos cómo se produce todo esto. El código XHTML del ejemplo es el siguiente:


<h:form>
       <p:panel header="Spinners">
           <h:panelGrid id="grid" columns="2" cellpadding="5">
                <h:outputLabel for="spinnerBasic" value="Basic Spinner: " />
                <p:spinner id="spinnerBasic" value="#{spinnerController.number1}"/>
                <h:outputLabel for="spinnerStep" value="Step Factor: " />
                <p:spinner id="spinnerStep" value="#{spinnerController.number2}" stepFactor="0.25"/>
                <h:outputLabel for="minmax" value="Min/Max: " />
                <p:spinner id="minmax" value="#{spinnerController.number3}" min="0" max="100"/>
                <h:outputLabel for="prefix" value="Prefix: " />
                <p:spinner id="prefix" value="0" prefix="$" min="0" value="#{spinnerController.number4}"/>
           <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
           <p:outputPanel>
                   <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
                      <p:ajax update="ajaxspinnervalue" process="@this" />
               </p:spinner>
               <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
            </p:outputPanel>
           </h:panelGrid>
       </p:panel>
    <p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
    
    <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        ...
     </p:dialog>
</h:form>

En primer lugar, observemos que aparecen las etiquetas clásicas de JSF: <h:form> en la línea 1, <h:panelGrid> en la línea 3, <h:outputLabel> en la línea 4. Algunas etiquetas JSF son retomadas por PF y ampliadas: <p:commandButton> línea 21. A continuación, encontramos etiquetas PF de formato: <p:panel> línea 2, <p:outputPanel> línea 13, <p:dialog> línea 23. Por último, tenemos etiquetas de entrada: <p:spinner> línea 5.

Analicemos este código en relación con la vista:

  • en [1], el componente obtenido con la etiqueta <p:panel> de la línea 2,
  • en [2], el campo de entrada obtenido mediante la combinación de las etiquetas <p:outputLabel> y <p:spinner>, líneas 6 y 7,
  • en [3], el botón de POST obtenido con la etiqueta <p:commandButton> de la línea 21,
  • en [4], el cuadro de diálogo de las líneas 23-25,
  • en [5], un contenedor invisible para dos componentes. Se crea mediante la etiqueta <p:outputPanel> de la línea 13.

Analicemos el siguiente código que implementa una acción AJAX:


           <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
           <p:outputPanel>
                   <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
                      <p:ajax update="ajaxspinnervalue" process="@this" />
               </p:spinner>
               <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
</p:outputPanel>

Este código genera la siguiente vista:

  • línea 1: muestra el texto [1]. Al mismo tiempo, es una etiqueta para el componente id=ajaxspinner (atributo «for»). Este componente es el de la línea 3 (atributo «id»),
  • líneas 3-5: muestran el componente [2]. Este componente es un componente de entrada/visualización asociado al modelo #{spinnerController.number5} (atributo «value»),
  • línea 6: muestra el componente [3]. Este componente es un componente de visualización vinculado al modelo #{spinnerController.number5} (atributo «value»),
  • línea 4: la etiqueta <p:ajax> añade un comportamiento AJAX al spinner. Cada vez que este cambia de valor, se realiza un POST con dicho valor (atributo process="@this") en el modelo #{spinnerController.number5}. Una vez hecho esto, se actualiza la página (atributo update). Este atributo tiene como valor el identificador de un componente de la página, en este caso el de la línea 6. El componente de destino del atributo update se actualiza entonces con el modelo. Este vuelve a ser #{spinnerController.number5}, es decir, el valor de spinner. De este modo, el campo [3] sigue las entradas del campo [2].

Se trata de un comportamiento AJAX, acrónimo que significa «Asynchronous Javascript And XML». En general, un comportamiento AJAX es el siguiente:

  • el navegador muestra una página HTML que contiene código JavaScript (J de AJAX). Los elementos de la página forman un objeto JavaScript denominado DOM (Document Object Model),
  • el servidor aloja la aplicación web que ha generado esta página,
  • en [1], se produce un evento en la página. Por ejemplo, el incremento de spinner. Este evento es gestionado por JavaScript,
  • En [2], el JavaScript realiza un POST en la aplicación web. Lo hace de forma asíncrona (la «A» de AJAX). El usuario puede seguir trabajando con la página. No se bloquea, pero se puede bloquear si es necesario. El POST actualiza la plantilla de la página a partir de los valores enviados, en este caso la plantilla #{spinnerController.number5},
  • en [3]; la aplicación web devuelve a JavaScript una respuesta XML (la X de AJAX) o JSON (JavaScript, notación de objetos),
  • en [4], el JavaScript utiliza esta respuesta para actualizar una zona concreta del DOM, en este caso la zona de id=ajaxspinnervalue.

Cuando se utilizan JSF y PrimeFaces, el JavaScript lo genera PrimeFaces. Esta biblioteca se basa en la biblioteca de JavaScript JQuery. Del mismo modo, los componentes de Primefaces se basan en los de la biblioteca de componentes JQuery y UI (interfaz de usuario). Por lo tanto, JQuery constituye la base de Primefaces.

Volvamos a nuestro ejemplo y veamos ahora el POST del botón [Submit]:

El código asociado a POST es el siguiente:


<p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
    
    <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        <h:panelGrid id="display" columns="2" cellpadding="5">
            <h:outputText value="Value 1: " />
            <h:outputText value="#{spinnerController.number1}" /> 
            
            <h:outputText value="Value 2: " />
            <h:outputText value="#{spinnerController.number2}" /> 
            
            <h:outputText value="Value 3: " />
            <h:outputText value="#{spinnerController.number3}" /> 
            
            <h:outputText value="Value 4: " />
            <h:outputText value="#{spinnerController.number4}" /> 
            
            <h:outputText value="Value 5: " />
            <h:outputText value="#{spinnerController.number5}" /> 
        </h:panelGrid>
     </p:dialog>
                
</h:form>
  • línea 1: el POST es provocado por el botón de la línea 1. En Primefaces, las etiquetas que provocan un POST lo hacen, por defecto, en forma de una llamada AJAX. Por eso, estas etiquetas tienen un atributo «update» para indicar el campo que se debe actualizar una vez recibida la respuesta del servidor. En este caso, el campo que se actualiza es el panelGrid de la línea 4. Así pues, al recibir la respuesta del POST, este campo se actualizará con los valores enviados al modelo. Sin embargo, se encuentran dentro de un cuadro de diálogo que no es visible por defecto. Es el atributo «oncomplete» de la línea 1 el que lo muestra. Este evento se produce al finalizar el procesamiento del POST. El valor de este atributo es código JavaScript. Aquí se muestra el cuadro de diálogo que tiene el id=dialog, es decir, el de la línea 3 (atributo widgetVar),
  • línea 3: se ven varios atributos del cuadro de diálogo. Hay que probar para ver qué hacen.

Hemos hablado del modelo, pero aún no lo hemos presentado. Es este:

public class SpinnerController {

    private int number1;
    private double number2;
    private int number3;
    private int number4;
    private int number5;

     // métodos getter y setter
...
}

En general, se puede proceder de la siguiente manera:

  • localizar el componente de PrimeFaces que se quiere utilizar,
  • estudiar su ejemplo. Los ejemplos de Primefaces están bien elaborados y son fáciles de entender.

5.4. Un primer proyecto de Primefaces: mv-pf-01

Creemos un proyecto web de Maven con NetBeans:

  • [1, 2, 3]: creamos un proyecto Maven del tipo [Web Application],
  • [4]: el servidor será Tomcat,
  • en [5], el proyecto generado,
  • en [6], se elimina el archivo [index.jsp] y el paquete Java,
  • en [7, 8]: en las propiedades del proyecto, se añade compatibilidad con Java Server Faces,
  • en [9]; en la pestaña [Components], se selecciona la biblioteca de componentes PrimeFaces. NetBeans ofrece compatibilidad con otras bibliotecas de componentes: ICEFaces y RichFaces.
  • En [10], el proyecto generado. En [11], se observa la dependencia de Primefaces.

En resumen, un proyecto de Primefaces es un proyecto clásico de JSF al que se le ha añadido una dependencia de Primefaces. Nada más.

Una vez entendido esto, modificamos el archivo [pom.xml] para trabajar con las últimas versiones de las bibliotecas:


    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.1.8</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>3.3</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <repositories>
    <repository>
      <id>jsf20</id>
      <name>Repository for library Library[jsf20]</name>
      <url>http://download.java.net/maven/2/</url>
    </repository>
    <repository>
      <id>primefaces</id>
      <name>Repository for library Library[primefaces]</name>
      <url>http://repository.primefaces.org/</url>
    </repository>
</repositories>

En las líneas 26-30, fíjate en el repositorio de Maven para Primefaces. Una vez realizadas estas modificaciones, compilamos el proyecto para iniciar la descarga de las dependencias. De este modo, obtenemos el proyecto [12].

Ahora, intentemos reproducir el ejemplo que hemos estudiado. La página [index.html] queda así:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <h:head>
    <title>Spinner</title>
  </h:head>
  <h:body>
    <!-- formulario -->
    <h:form>
      <p:panel header="Spinners">
        <h:panelGrid id="grid" columns="2" cellpadding="5">
          <h:outputLabel for="spinnerBasic" value="Basic Spinner: " />
          <p:spinner id="spinnerBasic" value="#{spinnerController.number1}"/>
          <h:outputLabel for="spinnerStep" value="Step Factor: " />
          <p:spinner id="spinnerStep" value="#{spinnerController.number2}" stepFactor="0.25"/>
          <h:outputLabel for="minmax" value="Min/Max: " />
          <p:spinner id="minmax" value="#{spinnerController.number3}" min="0" max="100"/>
          <h:outputLabel for="prefix" value="Prefix: " />
          <p:spinner id="prefix" prefix="$" min="0" value="#{spinnerController.number4}"/>
          <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
          <p:outputPanel>
            <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
              <p:ajax update="ajaxspinnervalue" process="@this" />
            </p:spinner>
            <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
          </p:outputPanel>
        </h:panelGrid>
      </p:panel>
      <p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
      <!-- cuadro de diálogo -->
      <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        <h:panelGrid id="display" columns="2" cellpadding="5">
          <h:outputText value="Value 1: " />
          <h:outputText value="#{spinnerController.number1}" /> 
          <h:outputText value="Value 2: " />
          <h:outputText value="#{spinnerController.number2}" /> 
          <h:outputText value="Value 3: " />
          <h:outputText value="#{spinnerController.number3}" /> 
          <h:outputText value="Value 4: " />
          <h:outputText value="#{spinnerController.number4}" /> 
          <h:outputText value="Value 5: " />
          <h:outputText value="#{spinnerController.number5}" /> 
        </h:panelGrid>
      </p:dialog>
    </h:form>
  </h:body>
</html>

No hay que olvidar la línea 5, que declara el espacio de nombres de la biblioteca de etiquetas de Primefaces. Añadimos al proyecto el bean que sirve de plantilla para la página:

  

El bean es el siguiente:


package beans;

import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean
@RequestScoped
public class SpinnerController {

  // plantilla
  private int number1;
  private double number2;
  private int number3;
  private int number4;
  private int number5;

  // métodos getter y setter
  ...
}

La clase es un bean (línea 6) de ámbito de consulta (línea 7). Como no se ha especificado ningún nombre, el bean recibe el nombre de la clase con la primera letra en minúscula: spinnerController.

Al ejecutar el proyecto, se obtiene lo siguiente:

 

Acabamos de mostrar cómo probar un ejemplo tomado de la página web de Primefaces. Todos los ejemplos se pueden probar de esta manera.

A continuación, nos centraremos únicamente en algunos componentes de Primefaces. En primer lugar, retomaremos los ejemplos estudiados con JSF y sustituiremos algunas etiquetas JSF por etiquetas de Primefaces. El aspecto de las páginas se modificará ligeramente; tendrán un comportamiento AJAX, pero no será necesario cambiar los beans asociados. En cada uno de los ejemplos que siguen, nos limitaremos a presentar el código de las páginas y las capturas de pantalla correspondientes. Se invita al lector a probar los ejemplos para detectar las diferencias entre las páginas y las páginas.

5.5. Ejemplo mv-pf-02: gestor de eventos – internacionalización – navegación entre páginas

Este proyecto es la adaptación del proyecto JSF [mv-jsf2-02] (apartado 2.4, página 41):

El proyecto NetBeans es el siguiente:

La página [index.html] es la siguiente:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </h:head>
    <body>
      <h:form id="formulaire">
        <h:panelGrid columns="2">
          <p:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}" ajax="false"/>
          <p:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}" ajax="false"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
        <p:commandLink value="#{msg['welcome.page1']}" action="page1" ajax="false"/>
      </h:form>
    </body>
  </f:view>
</html>

En las líneas 15, 16 y 19, las etiquetas <h:commandLink> se han sustituido por etiquetas <p:commandLink>. Esta etiqueta tiene un comportamiento predeterminado AJAX que se puede desactivar estableciendo el atributo ajax="false". Por lo tanto, en este caso, las etiquetas <p:commandLink> se comportan como etiquetas <h:commandLink>: la página se recargará al hacer clic en estos enlaces.

5.6. Ejemplo mv-pf-03: maquetación mediante facelets

Este proyecto muestra la creación de páginas XHTML utilizando las plantillas facelets del ejemplo [mv-jsf2-09] (apartado 2.11):

 

El proyecto de NetBeans es el siguiente:

  • en [1], los archivos de configuración del proyecto JSF,
  • en [2], las páginas de XHTML,
  • en [3], el bean de soporte para el cambio de idiomas,
  • en [4], los archivos de mensajes,
  • en [5], las dependencias.

Las páginas del proyecto siguen el modelo de la página [layout.xhtml]:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
    </h:head>
    <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
      <h:form id="formulaire">
        <table style="width: 600px">
          <tr>
            <td colspan="2" bgcolor="#ccccff">
              <ui:include src="entete.xhtml"/>
            </td>
          </tr>
          <tr>
            <td style="width: 100px; height: 200px" bgcolor="#ffcccc">
              <ui:include src="menu.xhtml"/>
            </td>
            <td>
              <p:outputPanel id="contenu">
                <ui:insert name="contenu" >
                  <h2>Contenu</h2>
                </ui:insert>
              </p:outputPanel>
            </td>
          </tr>
          <tr bgcolor="#ffcc66">
            <td colspan="2">
              <ui:include src="basdepage.xhtml"/>
            </td>
          </tr>         
        </table>
      </h:form>
    </h:body>
  </f:view>
</html>
  • línea 9: una etiqueta <f:view> enmarca toda la página para aprovechar la internacionalización que permite,
  • línea 15: un formulario con el identificador «formulario». Este formulario constituye el cuerpo de la página. En este cuerpo solo hay una parte dinámica, la de las líneas 28-30. Ahí es donde se insertará la parte variable de la página:
  • la zona enmarcada anteriormente se actualizará mediante llamadas a AJAX. Para identificarla, la hemos incluido en un contenedor PrimeFaces generado por la etiqueta <p:outputPanel> (línea 27). Y a este contenedor se le ha asignado el nombre «contenido» (atributo id). Dado que se encuentra en un formulario que, a su vez, es un contenedor con el nombre «formulario», el nombre completo del campo dinámico es:formulario:contenido. El primer «:» indica que se parte de la raíz del documento, luego se pasa al contenedor con el nombre «formulario» y, a continuación, al contenedor con el nombre «contenido». Una dificultad con AJAX es nombrar correctamente las zonas que se deben actualizar mediante una llamada a AJAX. Lo más sencillo es consultar el código fuente de la página HTML recibida:

1
2
3
            <td><span id="formulaire:contenu">
                  <h2>Contenu</h2></span>
</td>

En el ejemplo anterior, se observa que la etiqueta <h:outputPanel> ha generado una etiqueta HTML <span>. En este ejemplo, el nombre relativo «formulario:contenido» (sin el «:» inicial) y el nombre completo «:formulario:contenido» (con el «:» inicial) hacen referencia al mismo objeto.

Cabe destacar que las llamadas AJAX (<p:commandButton>, <p:commandLink>) que actualizan la zona dinámica tendrán el atributo update=":formulario:contenido".

La página [index.xhtml] es la única página que muestra el proyecto:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <ui:fragment rendered="#{requestScope.page1 || requestScope.page2==null}">
        <ui:include src="page1.xhtml"/>
      </ui:fragment>
      <ui:fragment rendered="#{requestScope.page2}">
        <ui:include src="page2.xhtml"/>
      </ui:fragment>
    </ui:define>
  </ui:composition>
</html>
  • En la línea 8, la plantilla de [index.xhtml] es la página [layout.xhtml] que acabamos de presentar;
  • línea 9: es el campo «id contenido» el que se actualiza mediante [index.html]. En este campo hay dos fragmentos:
    • el fragmento [page1.xhtml], línea 11;
    • el fragmento [page2.xhtml], línea 14.

Estos dos fragmentos son mutuamente excluyentes.

  • línea 10: el fragmento [page1.xhtml] se muestra si la consulta tiene el atributo «page1» establecido en «true» o si el atributo «page2» no existe. Este es el caso de la primera consulta, en la que ninguno de estos atributos estará presente en la consulta. En este caso, se mostrará el fragmento [page1.xhtml];
  • línea 11; el fragmento [page2.xhtml] se muestra si la consulta tiene el atributo «page2» con el valor «true»

El fragmento [page1.xhtml] es el siguiente:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <body>
    <h:panelGrid columns="2">
      <p:commandLink value="#{msg['page1.langue1']}" actionListener="#{changeLocale.setFrenchLocale}" ajax="true" update=":formulaire:contenu"/>
      <p:commandLink value="#{msg['page1.langue2']}" actionListener="#{changeLocale.setEnglishLocale}" ajax="true" update=":formulaire:contenu"/>
    </h:panelGrid>
    <h1><h:outputText value="#{msg['page1.titre']}" /></h1>
     <p:commandLink value="#{msg['page1.lien']}" update=":formulaire:contenu">
      <f:setPropertyActionListener value="#{true}" target="#{requestScope.page2}" />  
    </p:commandLink>
  </body>
</html>

y muestra el siguiente contenido:

  • Líneas 11 y 12: los dos enlaces para cambiar el idioma. Estos dos enlaces provocan llamadas a AJAX (ajax=true). Este es el valor por defecto. Por lo tanto, no es necesario incluir el atributo ajax=true. No lo volveremos a hacer en lo sucesivo. Cabe señalar que estos dos enlaces actualizan el área :formulario:contenido (atributo update), la que aparece enmarcada más arriba,
  • línea 15: un enlace de navegación AJAX que, de nuevo, actualiza el campo :formulario:contenido,
  • línea 16: se utiliza la etiqueta <h:setPropertyActionListener> para incluir el atributo page2 en la consulta con el valor true. Esto hará que se muestre el fragmento [page2.xhtml] (línea 6 más abajo) en la página [index.xhtml]:

  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <ui:fragment rendered="#{requestScope.page1 || requestScope.page2==null}">
        <ui:include src="page1.xhtml"/>
      </ui:fragment>
      <ui:fragment rendered="#{requestScope.page2}">
        <ui:include src="page2.xhtml"/>
      </ui:fragment>
    </ui:define>
</ui:composition>

El fragmento [page2.xhtml] es similar:

El código de [page2.xhtml] es el siguiente:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <body>
    <h1><h:outputText value="#{msg['page2.entete']}"/></h1>
    <p:commandLink value="#{msg['page2.lien']}" update=":formulaire:contenu">
      <f:setPropertyActionListener value="#{true}" target="#{requestScope.page1}" />  
    </p:commandLink>
  </body>
</html>

De este ejemplo, tomaremos en cuenta los siguientes puntos para continuar:

  • utilizaremos la plantilla [layout.xhtml] como plantilla para las páginas,
  • la zona dinámica se identificará con el id:formulario:contenido y se actualizará mediante llamadas a AJAX.

5.7. Ejemplo mv-pf-04: formulario de introducción de datos

Este proyecto es la adaptación del proyecto JSF2 [mv-jsf2-03] (véase el apartado 2.5):

El proyecto de NetBeans es el siguiente:

Arriba, en [1], se muestran las páginas XHTML del proyecto. El diseño lo gestiona la plantilla [layout.xhtml] analizada anteriormente. La página [index.xhtml] es la única página del proyecto. Se muestra en el área :formulario:contenido. Su código es el siguiente:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <ui:include src="page1.xhtml"/>
    </ui:define>
  </ui:composition>
</html>

Se limita a mostrar el fragmento [page1.xhtml]. Este es el equivalente al formulario analizado en el ejemplo [mv-jsf2-03]. Recordemos que el objetivo de este último era presentar las etiquetas de entrada JSF. Estas etiquetas se han sustituido aquí por etiquetas de PrimeFaces.

PanelGrid

Para dar formato a los elementos de [page1.xhtml], utilizamos la etiqueta <p:panelGrid>. Por ejemplo, para los dos enlaces de idiomas:


<!-- idiomas -->
    <p:panelGrid columns="2">
      <p:commandLink value="#{msg['form.langue1']}" actionListener="#{changeLocale.setFrenchLocale}" update=":formulaire:contenu"/>
      <p:commandLink value="#{msg['form.langue2']}" actionListener="#{changeLocale.setEnglishLocale}" update=":formulaire:contenu"/>
    </p:panelGrid>

El resultado es el siguiente:

 

Otra variante de la etiqueta <p:panelGrid> es la siguiente:


<p:panelGrid>

      <f:facet name="header">  
        <p:row>  
          <p:column colspan="3"><h:outputText value="#{msg['form.titre']}"/></p:column>  
        </p:row>  
        <p:row>  
          <p:column><h:outputText value="#{msg['form.headerCol1']}"/></p:column>  
          <p:column><h:outputText value="#{msg['form.headerCol2']}"/></p:column>  
          <p:column><h:outputText value="#{msg['form.headerCol3']}"/></p:column>  
        </p:row>  
      </f:facet>        

      <p:row>
        <p:column>
          <h:outputText value="inputText"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputText" value="#{msg['form.loginPrompt']}" />  
          <p:inputText id="inputText" value="#{form.inputText}"/>
        </p:column>
        <p:column>
          <h:outputText id="inputTextValue" value="#{form.inputText}"/>
        </p:column>
      </p:row>
...
     <f:facet name="footer">
        <p:row>
          <p:column colspan="3">
            <div align="center">
              <p:commandButton value="#{msg['form.submitText']}" update=":formulaire:contenu"/>
            </div>
          </p:column>
        </p:row>
      </f:facet>    
</p:panelGrid>

Las filas y columnas de la tabla se identifican mediante las etiquetas <p:row> y <p:column>.

Las líneas 3-12 definen el encabezado de la tabla:

 

Las líneas 14-25 definen una fila de la tabla:

 

Las líneas 27-35 definen el pie de página de la tabla:

 

inputText


      <p:row>
        <p:column>
          <h:outputText value="inputText"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputText" value="#{msg['form.loginPrompt']}" />  
          <p:inputText id="inputText" value="#{form.inputText}"/>
        </p:column>
        <p:column>
          <h:outputText id="inputTextValue" value="#{form.inputText}"/>
        </p:column>
</p:row>
 

contraseña


<p:row>
        <p:column>
          <h:outputText value="inputSecret"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputSecret" value="#{msg['form.passwdPrompt']}"/>
          <p:password id="inputSecret" value="#{form.inputSecret}" feedback="true"   
               promptLabel="#{msg['form.promptLabel']}" weakLabel="#{msg['form.weakLabel']}"  
               goodLabel="#{msg['form.goodLabel']}" strongLabel="#{msg['form.strongLabel']}" />  
        </p:column>
        <p:column>
          <h:outputText id="inputSecretValue" value="#{form.inputSecret}"/>
        </p:column>
      </p:row>

En la línea 7, el atributo feedback=true permite obtener información sobre la calidad de la contraseña. [1]

inputTextArea


<p:row>
        <p:column>
          <h:outputText value="inputTextArea"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputTextArea" value="#{msg['form.descPrompt']}"/>
          <p:editor id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
        </p:column> 
        <p:column>
          <h:outputText id="inputTextAreaValue" value="#{form.inputTextArea}"/>
        </p:column>
      </p:row>

Línea 7: la etiqueta <p:editor> muestra un editor enriquecido que permite dar formato al texto (fuente, tamaño, color, alineación, etc.). Lo que se envía al servidor es el código HTML del texto introducido [2].

selectOneListBox


<p:row>
        <p:column>
          <h:outputText value="selectOneListBox"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectOneListBox1" value="#{msg['form.selectOneListBox1Prompt']}"/>
          <p:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}">
            <f:selectItem itemValue="1" itemLabel="un"/>
            <f:selectItem itemValue="2" itemLabel="deux"/>
            <f:selectItem itemValue="3" itemLabel="trois"/>
          </p:selectOneListbox>
        </p:column>
        <p:column>
          <h:outputText id="selectOneListBox1Value" value="#{form.selectOneListBox1}"/>
        </p:column>
      </p:row>
 

selectOneMenu


<p:row>
        <p:column>
          <h:outputText value="selectOneMenu"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectOneMenu" value="#{msg['form.selectOneMenuPrompt']}"/>
          <p:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
            <f:selectItem itemValue="1" itemLabel="un"/>
            <f:selectItem itemValue="2" itemLabel="deux"/>
            <f:selectItem itemValue="3" itemLabel="trois"/>
            <f:selectItem itemValue="4" itemLabel="quatre"/>
            <f:selectItem itemValue="5" itemLabel="cinq"/>
          </p:selectOneMenu>
        </p:column>
        <p:column>
          <h:outputText id="selectOneMenuValue" value="#{form.selectOneMenu}"/>
        </p:column>
      </p:row>
 

selectManyMenu


<p:row>
        <p:column>
          <h:outputText value="selectManyMenu"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectManyMenu" value="#{msg['form.selectManyMenuPrompt']}"/>
          <p:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
            <f:selectItem itemValue="1" itemLabel="un"/>
            <f:selectItem itemValue="2" itemLabel="deux"/>
            <f:selectItem itemValue="3" itemLabel="trois"/>
            <f:selectItem itemValue="4" itemLabel="quatre"/>
            <f:selectItem itemValue="5" itemLabel="cinq"/>
          </p:selectManyMenu>
          <p:commandLink value="#{msg['form.buttonRazText']}" actionListener="#{form.clearSelectManyMenu()}" update=":formulaire:selectManyMenu" style="margin-left: 10px"/>
        </p:column>
        <p:column>
          <h:outputText id="selectManyMenuValue" value="#{form.selectManyMenuValue}"/>
        </p:column>
      </p:row>
 

En la línea 14, cabe señalar que el enlace [Raz] actualiza AJAX del campo :formulario:selectManyMenu, que corresponde al componente de la línea 6. No obstante, hay que tener en cuenta que durante el proceso POST AJAX, se envían todos los valores del formulario. Por lo tanto, se actualiza la totalidad del modelo. Sin embargo, con este modelo solo se actualiza el campo :formulario:selectManyMenu.

selectBooleanCheckbox


<p:row>
        <p:column>
          <h:outputText value="selectBooleanCheckbox"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectBooleanCheckbox" value="#{msg['form.selectBooleanCheckboxPrompt']}"/>
          <p:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/>
        </p:column>
        <p:column>
          <h:outputText id="selectBooleanCheckboxValue" value="#{form.selectBooleanCheckbox}"/>
        </p:column>
      </p:row>
 

selectManyCheckbox


<p:row>
        <p:column>
          <h:outputText value="selectManyCheckbox"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectManyCheckbox" value="#{msg['form.selectManyCheckboxPrompt']}"/>
          <p:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
            <f:selectItem itemValue="1" itemLabel="rouge"/>
            <f:selectItem itemValue="2" itemLabel="bleu"/>
            <f:selectItem itemValue="3" itemLabel="blanc"/>
            <f:selectItem itemValue="4" itemLabel="noir"/>
          </p:selectManyCheckbox>
        </p:column>
        <p:column>
          <h:outputText id="selectManyCheckboxValue" value="#{form.selectManyCheckboxValue}"/>
        </p:column>
      </p:row>
 

selectOneRadio


<p:row>
        <p:column>
          <h:outputText value="selectOneRadio"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectOneRadio" value="#{msg['form.selectOneRadioPrompt']}"/>
          <p:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}" >
            <f:selectItem itemValue="1" itemLabel="voiture"/>
            <f:selectItem itemValue="2" itemLabel="vélo"/>
            <f:selectItem itemValue="3" itemLabel="scooter"/>
            <f:selectItem itemValue="4" itemLabel="marche"/>
          </p:selectOneRadio>
        </p:column>
        <p:column>
          <h:outputText id="selectOneRadioValue" value="#{form.selectOneRadio}"/>
        </p:column>
      </p:row>
 

5.8. Ejemplo: mv-pf-05: listas dinámicas

Este proyecto es la adaptación del proyecto JSF2 [mv-jsf2-04] (véase el apartado 2.6):

Image

Este proyecto no introduce nuevas etiquetas de PrimeFaces con respecto al proyecto anterior. Por lo tanto, no lo comentaremos. Forma parte de la lista de ejemplos que se pone a disposición del lector en la página web del documento.

5.9. Ejemplo: mv-pf-06: navegación – sesión – gestión de excepciones

Este proyecto es la adaptación del proyecto JSF2 [mv-jsf2-05] (véase el apartado 2.7):

Una vez más, este ejemplo no introduce nuevas etiquetas de PrimeFaces. Solo comentaremos la tabla de enlaces que aparece en el recuadro anterior:


<p:panelGrid columns="6">
  <p:commandLink value="1" action="form1?faces-redirect=true" ajax="false"/>
  <p:commandLink value="2" action="#{form.doAction2}" ajax="false"/>
  <p:commandLink value="3" action="form3?faces-redirect=true" ajax="false"/>
  <p:commandLink value="4" action="#{form.doAction4}" ajax="false"/>
  <p:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}" ajax="false"/>
  <p:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}" ajax="false"/>
</p:panelGrid>
  • Todos los enlaces tienen el atributo ajax=false. Por lo tanto, la carga de la página es normal;
  • fíjate en las líneas 2 y 4, donde se muestra cómo realizar una redirección.

5.10. Ejemplo: mv-pf-07: validación y conversión de los datos introducidos

Este proyecto es la adaptación del proyecto JSF2 [mv-jsf2-06] (véase el apartado 2.8):

Image

La aplicación introduce dos nuevas etiquetas, la etiqueta <p:messages>:


<p:messages globalOnly="true"/>

Image

y la etiqueta <p:message>:


<p:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<p:message for="saisie1" styleClass="error"/>

En comparación con la etiqueta <h:message> de JSF, la etiqueta <p:message> de PF introduce los siguientes cambios:

  • el aspecto del mensaje de error es diferente en [1],
  • el campo de entrada con error aparece rodeado por un recuadro rojo en [2].

5.11. Ejemplo: mv-pf-08: eventos relacionados con el cambio de estado de los componentes

Este proyecto es la adaptación del proyecto JSF2 [mv-jsf2-07] (véase el apartado 2.9):

Image

El proyecto JSF introducía el concepto de listeners. La gestión de listener con Primefaces se llevó a cabo de forma diferente.

Con JSF:

1
2
3
4
5
6
7
         <!-- línea 1 -->
        <h:outputText value="#{msg['combo1.prompt']}"/>
        <h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
          <f:selectItems value="#{form.combo1Items}"/>
        </h:selectOneMenu>
        <h:panelGroup></h:panelGroup>
<h:outputText value="#{form.combo1}"/>

Con Primefaces:

<h:outputText value="#{msg['combo1.prompt']}"/>
        <p:selectOneMenu id="combo1" value="#{form.combo1}" styleClass="combo">
          <f:selectItems value="#{form.combo1Items}"/>
          <p:ajax update=":formulaire:combo2"/>  
        </p:selectOneMenu>
        <h:panelGroup></h:panelGroup>
        <h:outputText value="#{form.combo1}"/>

        <h:outputText value="#{msg['combo2.prompt']}"/>
        <p:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
          <f:selectItems value="#{form.combo2Items}"/>
        </p:selectOneMenu>
  • línea 2: la etiqueta <h:selectOneMenu> sin el atributo valueChangeListener,
  • línea 4: la etiqueta <p:ajax> añade un comportamiento AJAX a su etiqueta principal <h:selectOneMenu>. Por defecto, reacciona al evento «cambio de valor» de la lista combo1. Al producirse este evento, los valores del formulario al que pertenece se enviarán al servidor mediante una llamada AJAX. De este modo, se actualiza el modelo. Se utiliza este nuevo modelo para actualizar la lista desplegable identificada por combo2 (línea 10). Cabe señalar, en la línea 4, que la llamada AJAX no ejecuta ningún método del modelo. En este caso, es innecesario. Simplemente queremos modificar el modelo mediante POST con los valores introducidos.

5.12. Ejemplo: mv-pf-09: introducción asistida

Este proyecto presenta campos de entrada específicos de PrimeFaces que facilitan la introducción de determinados tipos de datos:

5.12.1. El proyecto de NetBeans

El proyecto de NetBeans es el siguiente:

El interés del proyecto radica en:

  • la única página [index.html] que muestra,
  • la plantilla [Form.java] de dicha página.

5.12.2. La plantilla

El formulario presenta cuatro campos de entrada asociados a la siguiente plantilla:


package forms;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean
@SessionScoped
public class Form implements Serializable {

  private Date calendrier;
  private Integer slider = 100;
  private Integer spinner = 1;
  private String autocompleteValue;

  public Form() {
  }

  public List<String> autocomplete(String query) {
    ...
  }
   // métodos getter y setter
...
}

Las cuatro entradas están asociadas a los campos de las líneas 14-17.

5.12.3. El formulario

El formulario es el siguiente:


<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre']}"/></h2>
      <p:growl id="messages" autoUpdate="true"/>
      <p:panelGrid columns="3" columnClasses="col1,col2,col3,col4">
        <h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
        <h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
        <h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>

        <!-- calendario -->
              ...

        <!-- control deslizante -->
              ...

        <!-- spinner -->
              ...

        <!-- autocompletado -->
              ...

      </p:panelGrid>
    </ui:define>
  </ui:composition>
</html>

Analicemos los cuatro campos de introducción de datos.

5.12.4. El calendario

La etiqueta <p:calendar> permite seleccionar una fecha en un calendario. Esta etiqueta admite diferentes atributos.


<h:outputText value="#{msg['calendar.prompt']}"/>
        <p:calendar id="calendrier" value="#{form.calendrier}" pattern="dd/MM/yyyy" timeZone="Europe/Paris"/>
        <h:outputText id="calendrierValue" value="#{form.calendrier}">
          <f:convertDateTime pattern="dd/MM/yyyy" type="date" timeZone="Europe/Paris"/>
        </h:outputText>

En la línea 2, se indica que la fecha debe mostrarse en el formato «dd/mm/aaaa» y que la zona horaria es la de París. Al colocar el cursor en el campo de entrada, aparece un calendario:

 

5.12.5. El control deslizante

La etiqueta <p:slider> permite introducir un número entero deslizando un cursor a lo largo de una barra:

 

El código de la etiqueta es el siguiente:


        <h:outputText value="#{msg['slider.prompt']}"/>
        <h:panelGrid columns="1" style="margin-bottom:10px">  
          <p:inputText id="slider" value="#{form.slider}" required="true" requiredMessage="#{msg['slider.required']}" validatorMessage="#{msg['slider.invalide']}">  
            <f:validateLongRange minimum="100" maximum="200"/>
          </p:inputText>
          <p:slider for="slider" minValue="100" maxValue="200"/>  
        </h:panelGrid>  
<h:outputText id="sliderValue" value="#{form.slider}"/>
  • línea 3: aquí tenemos una etiqueta <p:inputText> clásica que permite introducir el número entero. Este también se puede introducir mediante el control deslizante,
  • línea 4: la etiqueta <p:slider> está asociada a la etiqueta de entrada <p:inputText> (atributo «for»). Se le asigna un valor mínimo y un valor máximo.

5.12.6. El spinner

Ya hemos tenido ocasión de presentar este componente:


        <h:outputText value="#{msg['spinner.prompt']}"/>
        <p:spinner id="spinner" min="1" max="12" value="#{form.spinner}" required="true" requiredMessage="#{msg['spinner.required']}" validatorMessage="#{msg['spinner.invalide']}">
          <f:validateLongRange minimum="1" maximum="12"/>
        </p:spinner>
<h:outputText id="spinnerValue" value="#{form.spinner}"/>

Línea 3: el spinner permite introducir un número entero entre 1 y 12. Se puede introducir el número directamente en el campo de entrada del spinner o bien utilizar las flechas para aumentar o disminuir el número introducido.

 

5.12.7. La entrada asistida

La entrada asistida consiste en escribir los primeros caracteres de lo que se desea introducir. A continuación, aparecen sugerencias en una lista desplegable. Se puede seleccionar una de ellas. Este componente se utiliza en lugar de las listas desplegables cuando estas tienen un contenido demasiado extenso. Supongamos que queremos ofrecer una lista desplegable de las ciudades de Francia. Son varios miles de ciudades. Si dejamos que el usuario escriba los tres primeros caracteres del nombre de la ciudad, podemos ofrecerle una lista reducida de las ciudades que comienzan por esos caracteres.

 

El código de este componente es el siguiente:


        <h:outputText value="#{msg['autocomplete.prompt']}"/>
        <p:autoComplete value="#{form.autocompleteValue}" completeMethod="#{form.autocomplete}" required="true" requiredMessage="#{msg['autocomplete.required']}"/>
        <h:outputText id="autocompleteValue" value="#{form.autocompleteValue}"/>
        <h:panelGroup/>
        <h:panelGroup>
        <center><p:commandLink value="#{msg['valider']}" update="formulaire:contenu"/></center>
        </h:panelGroup>
<h:panelGroup/>

La etiqueta <p:autoComplete> de la línea 2 es la que permite la entrada asistida. El parámetro que nos interesa aquí es el atributo completeMethod, cuyo valor es el nombre de un método del modelo, encargado de ofrecer sugerencias que se correspondan con los caracteres introducidos por el usuario. Este método es el siguiente:


  public List<String> autocomplete(String query) {
    List<String> results = new ArrayList<String>();

    for (int i = 0; i < 10; i++) {
      results.add(query + i);
    }

    return results;
}

  • línea 1: el método recibe como parámetro la cadena de caracteres introducida por el usuario en el campo de entrada. Devuelve una lista de sugerencias,
  • líneas 4-6: se crea una lista de 10 propuestas que recoge los caracteres recibidos como parámetros y les añade un número del 0 al 9.

5.12.8. La etiqueta <p:growl>

La etiqueta <p:growl> es una alternativa posible a la etiqueta <p:messages>, que muestra los mensajes de error del formulario.


      <p:growl id="messages" autoUpdate="true"/>

En el ejemplo anterior, no se utiliza el atributo id. El atributo autoUpdate=true indica que la lista de mensajes de error debe actualizarse cada vez que se envíe el formulario.

Supongamos que se valida el siguiente formulario [1]:

  • en [2], la etiqueta <p:growl> mostrará entonces los mensajes de error asociados a los datos introducidos incorrectamente.

5.13. Ejemplo: mv-pf-10: dataTable - 1

Este proyecto presenta la etiqueta <p:dataTable>, que sirve para mostrar listas de datos

Image

5.13.1. El proyecto NetBeans

El proyecto de NetBeans es el siguiente:

El interés del proyecto radica en:

  • la única página [index.html] que muestra,
  • el modelo [Form.java] de esta última y el bean [Personne].

5.13.2. El archivo de mensajes

El archivo [messages_fr.properties] es el siguiente:


app.titre=intro-08
app.titre2=DataTable - 1
submit=Valider
personnes.headers.id=Id
personnes.headers.nom=Nom
personnes.headers.prenom=Pr\u00e9nom
layout.hautdepage=Primefaces en fran\u00e7ais
layout.menu=Menu fran\u00e7ais
layout.basdepage=ISTIA, universit\u00e9 d'Angers
form.langue1=Fran\u00e7ais
form.langue2=Anglais
form.noData=La liste des personnes est vide
form.listePersonnes=Liste de personnes
form.action=Action

5.13.3. La plantilla

El bean [Personne] representa a una persona:


package forms;

import java.io.Serializable;

public class Personne implements Serializable{
  // datos
  private int id;
  private String nom;
  private String prénom;
  
  // fabricantes
  public Personne(){
    
  }
  
  public Personne(int id, String nom, String prénom){
    this.id=id;
    this.nom=nom;
    this.prénom=prénom;
  }
  
  // toString
  public String toString(){
    return String.format("Personne[%d,%s,%s]", id,nom,prénom);
  }
  
  // getter y setters
...
}

La plantilla de la página [index.xhtml] es la siguiente clase [Form]:


package forms;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class Form implements Serializable{

  // modelo
  private List<Personne> personnes;
  private int personneId;

  // constructor
  public Form() {
    // inicialización de la lista de personas
    personnes = new ArrayList<Personne>();
    personnes.add(new Personne(1, "dupont", "jacques"));
    personnes.add(new Personne(2, "durand", "élise"));
    personnes.add(new Personne(3, "martin", "jacqueline"));
  }

  public void retirerPersonne() {
...
  }
  
  // getter y setter
...  
}
  • líneas 9-10: el bean tiene alcance de sesión,
  • líneas 18-24: el constructor crea una lista de tres personas, lista que, por lo tanto, se mantendrá a lo largo de las solicitudes,
  • línea 15: el número de una persona que se va a eliminar de la lista,
  • líneas 26-28: el método de eliminación.

5.13.4. El formulario

El formulario es el siguiente: [index.xhtml]:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
      <p:dataTable value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}">
        <f:facet name="header">  
           #{msg['form.listePersonnes']}  
        </f:facet>  
        <p:column>
          <f:facet name="header">
             #{msg['personnes.headers.id']}
          </f:facet>
           #{personne.id}
        </p:column>
        <p:column>
          <f:facet name="header">
             #{msg['personnes.headers.nom']}
          </f:facet>
           #{personne.nom}
        </p:column>
        <p:column>
          <f:facet name="header">
             #{msg['personnes.headers.prenom']}
          </f:facet>
           #{personne.prénom}
        </p:column>
        <p:column>
          <f:facet name="header">
             #{msg['form.action']}
          </f:facet>
          <p:commandLink value="Retirer" action="#{form.retirerPersonne}" update=":formulaire:contenu">
            <f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
          </p:commandLink>
        </p:column>
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>

Esto genera la siguiente vista (recuadro a continuación):

  • línea 12: genera la tabla que aparece en el recuadro anterior. El atributo «value» indica la colección que muestra la tabla, en este caso la lista de personas del modelo. El atributo emptyMessage es opcional. Indica el mensaje que se mostrará cuando la lista esté vacía. Por defecto es «no records found». En este caso, será:
 
  • líneas 13-15: generan el encabezado [1],
  • líneas 16-21: generan la columna [2],
  • líneas 22-27: generan la columna [3],
  • líneas 28-33: generan la columna [4],
  • líneas 34-41: generan la columna [5].

El enlace [Retirer] permite eliminar a una persona de la lista. En la línea [38], es el método [Form].retirerPersonne el que realiza esta tarea. Necesita conocer el número de la persona que se va a eliminar. Este se le proporciona en la línea 39. En la línea 38 se ha utilizado el atributo «action». En otras ocasiones, se ha utilizado el atributo actionListener. No estoy seguro de entender bien la diferencia funcional entre estos dos atributos. Sin embargo, en la práctica se observa que los atributos establecidos mediante las etiquetas <setPropertyActionListener> se fijan antes de la ejecución del método designado por el atributo «action», y que este no es el caso del atributo actionListener. En resumen, en cuanto haya parámetros que enviar a la acción llamada, hay que utilizar el atributo «action».

El método para eliminar a una persona es el siguiente:


...
@ManagedBean
@SessionScoped
public class Form implements Serializable{

  // modelo
  private List<Personne> personnes;
  private int personneId;

  public void retirerPersonne() {
    // se busca a la persona seleccionada
    int i = 0;
    for (Personne personne : personnes) {
      // ¿Persona actual = persona seleccionada?
      if (personne.getId() == personneId) {
        // se elimina la persona actual de la lista
        personnes.remove(i);
        // se ha terminado
        break;
      } else {
        // persona siguiente
        i++;
      }
    }
  }
...  
}

5.14. Ejemplo: mv-pf-11: dataTable - 2

Este proyecto presenta una tabla que muestra una lista de datos en la que se puede seleccionar una fila:

Al seleccionar una fila de la tabla, en el momento del POST, se envía información al modelo sobre la fila seleccionada. Por lo tanto, ya no es necesario un enlace [Retirer] por persona. Basta con uno solo para toda la tabla.

El proyecto de NetBeans es idéntico al anterior, salvo por algunos detalles: el formulario y su modelo. El formulario [index.xhtml] es el siguiente:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
      <p:dataTable value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   rowKey="#{personne.id}"  selection="#{form.personneChoisie}" selectionMode="single">
        ...
      </p:dataTable>
      <p:commandLink value="Retirer" action="#{form.retirerPersonne}" update=":formulaire:contenu"/>
    </ui:define>
  </ui:composition>
</html>
  • línea 13: el atributo selectionMode permite elegir un modo de selección single o multiple. En este caso, hemos optado por seleccionar solo una línea,
  • línea 13: el atributo rowkey designa un atributo de los elementos mostrados que permite seleccionarlos de forma única. En este caso, hemos elegido el id de la persona seleccionada,
  • línea 13: el atributo «selection» designa el atributo del modelo que recibirá una referencia de la persona seleccionada. Gracias al atributo «rowkey» anterior, en el lado del servidor se podrá calcular una referencia de la persona seleccionada. No disponemos de los detalles del método utilizado. Podemos imaginar que la colección se recorre secuencialmente en busca del elemento correspondiente al rowkey seleccionado. Esto significa que, si el método que asocia rowkey con selection es más complejo, dicho método no es utilizable,

Una vez explicado esto, el método [Form].retirerPersonne evoluciona de la siguiente manera:


...

@ManagedBean
@SessionScoped
public class Form implements Serializable {

  // plantilla
  private List<Personne> personnes;
  private Personne personneChoisie;

  // fabricante
  public Form() {
  ...
  }

  public void retirerPersonne() {
    // se elimina la persona seleccionada
    personnes.remove(personneChoisie);
  }

  // getters y setters
...
}
  • línea 9: en cada POST, la referencia de la línea 9 se inicializa con la referencia, en la lista de la línea 8, de la persona seleccionada,
  • en 18: esto simplifica la eliminación de la persona. La búsqueda que habíamos realizado en el ejemplo anterior se llevó a cabo mediante la etiqueta <dataTable>.

5.15. Ejemplo: mv-pf-12: dataTable - 3

Este proyecto es similar al anterior. La vista es, en particular, idéntica:

Image

El proyecto de NetBeans es idéntico al anterior, salvo por algunos detalles que vamos a repasar. El formulario [index.xhtml] evoluciona de la siguiente manera:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
      <p:dataTable value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   selectionMode="single" selection="#{form.personneChoisie}">
  ...
      </p:dataTable>
      <p:commandLink value="Retirer" action="#{form.retirerPersonne}" update=":formulaire:contenu"/>
    </ui:define>
  </ui:composition>
</html>
  • En la línea 6, el atributo «rowkey» ha desaparecido, pero el atributo «selection» se mantiene. La relación entre los atributos «rowkey» y «selection» se establece ahora a través de una clase. El atributo «value» de la línea 5 tiene ahora como valor una instancia de la interfaz Primefaces SelectableDataModel<T>. El método [Form].getPersonnes del modelo cambia de la siguiente manera:

  public DataTableModel getPersonnes() {
    return new DataTableModel(personnes);
}

De este modo, se añade un nuevo bean al proyecto:

Este bean es el siguiente:


package forms;

import java.util.List;
import javax.faces.model.ListDataModel;
import org.primefaces.model.SelectableDataModel;

public class DataTableModel extends ListDataModel<Personne> implements SelectableDataModel<Personne> {

  // constructores
  public DataTableModel() {
  }

  public DataTableModel(List<Personne> personnes) {
    super(personnes);
  }

  @Override
  public Object getRowKey(Personne personne) {
    return personne.getId();
  }

  @Override
  public Personne getRowData(String rowKey) {
    // lista de personas
    List<Personne> personnes = (List<Personne>) getWrappedData();
    // la clave es un entero 
    int key = Integer.parseInt(rowKey);
    // se busca a la persona seleccionada
    for (Personne personne : personnes) {
      if (personne.getId() == key) {
        return personne;
      }
    }
    // no se ha encontrado nada
    return null;
  }
}
  • línea 7: la clase es una instancia de la interfaz SelectableDataModel. Al menos dos clases implementan esta interfaz: ListDataModel, cuyo constructor admite una lista como parámetro, y ArrayDataModel, cuyo constructor admite un array como parámetro. En este caso, nuestro bean extiende la clase ListDataModel,
  • líneas 13-15: el constructor admite como parámetro la lista de personas que gestionamos. Este parámetro se pasa a la clase padre,
  • línea 18: el método getRowKey desempeña la función del atributo rowkey que se ha eliminado. Debe devolver el objeto que permite identificar a una persona de forma única, en este caso el id de la persona,
  • línea 23: el método getRowData debe devolver el objeto seleccionado a partir de su rowkey. Por lo tanto, en este caso, debe devolver una persona a partir de su ID. La referencia así obtenida se asignará al objeto de destino del atributo «selection» en la etiqueta dataTable, en este caso el atributo «selection="#{form.personneChoisie}"». El parámetro del método es el rowkey del objeto seleccionado por el usuario, en forma de cadena de caracteres,
  • líneas 24-35: devuelven la referencia de la persona cuyo ID se ha recibido. Esta referencia se asignará al modelo [Form].personneChoisie. Por lo tanto, el método [retirerPersonne] permanece sin cambios:

  public void retirerPersonne() {
    // se elimina a la persona elegida
    personnes.remove(personneChoisie);
  }

Esta es la técnica que hay que utilizar cuando la relación entre los atributos «rowkey» y «selection» no es una simple relación de propiedad («rowkey») a objeto («selection»).

5.16. Ejemplo: mv-pf-13: dataTable - 4

Este proyecto es similar al anterior, salvo que cambia el modo de selección de la persona que se va a dar de baja:

Image

En el ejemplo anterior, vemos que el objeto se selecciona mediante un menú contextual (clic con el botón derecho). Se solicita una confirmación de la eliminación:

 

El formulario [index.xhtml] evoluciona de la siguiente manera:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">

      <!-- título -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>

      <!-- menú contextual -->
      <p:contextMenu for="personnes">  
        <p:menuitem value="#{msg['form.supprimer']}" onclick="confirmation.show()"/>
      </p:contextMenu>  

      <!-- cuadro de diálogo -->
      <p:confirmDialog widgetVar="confirmation" message="#{msg['form.suppression.confirmation']}"  
                       header="#{msg['form.suppression.message']}" severity="alert" >                   
        <p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu"                          action="#{form.retirerPersonne}" oncomplete="confirmation.hide()"/>
        <p:commandButton value="#{msg['form.supprimer.non']}" onclick="confirmation.hide()" type="button" />                
      </p:confirmDialog>  

      <!-- dataTable-->
      <p:dataTable id="personnes" value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   selection="#{form.personneChoisie}" selectionMode="single">
        ...
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • líneas 9-11: definen un menú contextual para (atributo «for») el dataTable de la línea 21 (atributo «id»). Por lo tanto, al hacer clic con el botón derecho del ratón sobre la tabla de personas aparece este menú contextual,
  • línea 10: nuestro menú solo tiene una opción (etiqueta menuItem). Al hacer clic en esta opción, se ejecuta el código JavaScript del atributo onclick. El código JavaScript [confirmation.show()] muestra el cuadro de diálogo de la línea 14 (atributo widgetVar). Este es el siguiente:
  • línea 14: el atributo «message» muestra [3], el atributo «header» muestra [1], el atributo «severity» muestra el icono [2],
  • línea 16: muestra [4]. Al hacer clic, se elimina a la persona (atributo «action») y, a continuación, se cierra el cuadro de diálogo (atributo «oncomplete»). El atributo «oncomplete» es código JavaScript que se ejecuta una vez que se ha llevado a cabo la acción del lado del servidor,
  • línea 17: muestra [5]. Al hacer clic, se cierra el cuadro de diálogo y no se elimina a la persona.

5.17. Ejemplo: mv-pf-14: dataTable - 5

Este proyecto muestra que es posible obtener una respuesta del servidor tras la ejecución de una llamada AJAX. Para ello, se utiliza el atributo oncomplete de la llamada AJAX:

 

El formulario [index.xhtml] evoluciona de la siguiente manera:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
...
<!-- cuadro de diálogo 1 -->
      <p:confirmDialog widgetVar="confirmation" ... >                   
        <p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu" action="#{form.retirerPersonne}" oncomplete="handleRequest(xhr, status, args);confirmation.hide()"/>
        <p:commandButton ... />                
      </p:confirmDialog>  

      <!-- JavaScript -->
      <script type="text/javascript">  
        function handleRequest(xhr, status, args) {  
          // ¿error?
          if(args.msgErreur) {  
            alert(args.msgErreur);  
          }  
        }  
      </script> 
...
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • línea 7: el atributo «oncomplete» llama a la función JavaScript de las líneas 13-18,
  • línea 13: la firma del método debe ser esta. «args» es un diccionario que el modelo del lado del servidor puede enriquecer,
  • línea 15: se comprueba si el diccionario args tiene un atributo llamado «msgErreur». Si es así, se muestra (línea 16).

En el modelo, el método [retirerPersonne] se desarrolla de la siguiente manera:


public void retirerPersonne() {
    // eliminación aleatoria
    int i = (int) (Math.random() * 2);
    if (i == 0) {
      // se elimina a la persona seleccionada
      personnes.remove(personneChoisie);
    } else {
      // se devuelve un error
      String msgErreur = Messages.getMessage(null, "form.msgErreur", null).getSummary();
      RequestContext.getCurrentInstance().addCallbackParam("msgErreur", msgErreur);
    }
  }
  • línea 3: se genera un número aleatorio, 0 o 1,
  • líneas 4-6: si es 0, la persona seleccionada por el usuario se elimina de la lista de personas,
  • línea 9: en caso contrario, se genera un mensaje de error internacionalizado:

form.msgErreur=La personne n'a pu \u00eatre supprim\u00e9e. Veuillez r\u00e9essayer ult\u00e9rieurement.
form.msgErreur_detail=La personne n'a pu \u00eatre supprim\u00e9e. Veuillez r\u00e9essayer ult\u00e9rieurement.
  • línea 10: una instrucción compleja cuyo objetivo es añadir al diccionario «args», del que hemos hablado, el atributo denominado «msgErreur» con el valor «msgErreur» generado en la línea 9. A continuación, este atributo es recuperado por el método JavaScript de [index.xhtml]:

      <!-- JavaScript -->
      <script type="text/javascript">  
        function handleRequest(xhr, status, args) {  
          // ¿Error?
          if(args.msgErreur) {  
            alert(args.msgErreur);  
          }  
        }  
</script> 

5.18. Ejemplo: mv-pf-15: la barra de herramientas

En este proyecto estamos creando una barra de herramientas:

La barra de herramientas es el componente que aparece enmarcado arriba. Se obtiene con el código XHTML siguiente a [index.xhtml]:



<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <!-- título -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>

      <!-- barra de herramientas-->
      <p:toolbar>
        <p:toolbarGroup align="left">  
          ...  
        </p:toolbarGroup>
        <p:toolbarGroup align="right">  
          ...  
        </p:toolbarGroup>  
      </p:toolbar>
    </ui:define>
  </ui:composition>
</html>
  • líneas 15-22: la barra de herramientas,
  • líneas 16-18: definen el grupo de componentes a la izquierda de la barra,
  • líneas 19-21: lo mismo para los componentes de la derecha.

Los componentes situados a la izquierda de la barra de herramientas son los siguientes:


        <p:toolbarGroup align="left">  
          <h:outputText value="#{msg['form.etudiant']}"/>
          <p:spacer width="50px"/>
          <p:selectOneMenu value="#{form.personneId}" effect="fade">  
            <f:selectItems value="#{form.personnes}" var="personne" itemLabel="#{personne.prénom} #{personne.nom}" itemValue="#{personne.id}"/>  
          </p:selectOneMenu>              
          <p:separator/>
          <p:commandButton id="delete-personne" icon="ui-icon-trash" action="#{form.supprimerPersonne}" update=":formulaire:contenu"/>  
          <p:tooltip for="delete-personne" value="#{msg['form.delete.personne']}"/>  
</p:toolbarGroup>

Muestran la vista que se muestra a continuación:

  • línea 2: muestra [1],
  • línea 3: muestra un espacio de 30 píxeles [2],
  • líneas 4-6: muestran un menú desplegable con una lista de personas [3],
  • línea 7: muestra un separador [4],
  • línea 8: muestra un botón [5] que sirve para eliminar a la persona seleccionada en la lista desplegable. El botón tiene un icono. Estos iconos son los de JQuery y UI. Su lista se encuentra en URL, [http://jqueryui.com/themeroller/] y [6]:
  • Para conocer el nombre de un icono, basta con pasar el ratón por encima. A continuación, ese nombre se utiliza en el atributo «icon» del componente <commandButton>, por ejemplo, icon="ui-icon-trash". Cabe señalar que, en el ejemplo anterior, el nombre asignado será .ui-icon-trash y que se elimina el punto inicial de dicho nombre en el atributo icon,
  • línea 9: crea una burbuja de ayuda para el botón (atributo «for»). Al pasar el cursor por encima del botón, se muestra el mensaje de ayuda [7].

La plantilla asociada a estos componentes es la siguiente:


package forms;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class Form implements Serializable {

  // plantilla
  private List<Personne> personnes;
  private int personneId;

  // fabricante
  public Form() {
    // inicialización de la lista de personas
    personnes = new ArrayList<Personne>();
    personnes.add(new Personne(1, "dupont", "jacques"));
    personnes.add(new Personne(2, "durand", "élise"));
    personnes.add(new Personne(3, "martin", "jacqueline"));
  }

  public void supprimerPersonne() {
    // se busca a la persona seleccionada
    int i = 0;
    for (Personne personne : personnes) {
      // ¿Persona actual = persona seleccionada?
      if (personne.getId() == personneId) {
        // se elimina la persona actual de la lista
        personnes.remove(i);
        // Se ha completado
        break;
      } else {
        // persona siguiente
        i++;
      }
    }
  }

  // getters y setters
  ...
}

Los componentes situados a la derecha de la barra de herramientas son los siguientes:


<p:toolbar>
        <p:toolbarGroup align="left">  
          ... 
        </p:toolbarGroup>
        <p:toolbarGroup align="right">  
          <p:menuButton value="#{msg['form.options']}">  
            <p:menuitem id="menuitem-francais" value="#{msg['form.francais']}" actionListener="#{changeLocale.setFrenchLocale}" update=":formulaire"/>  
            <p:menuitem id="menuitem-anglais" value="#{msg['form.anglais']}" actionListener="#{changeLocale.setEnglishLocale}" update=":formulaire"/>  
          </p:menuButton>  
        </p:toolbarGroup>  
      </p:toolbar>

Muestran la vista que se muestra a continuación:

 
  • líneas 6-9: un botón de menú. Contiene opciones de menú,
  • línea 7: la opción para cambiar el formulario al francés,
  • línea 8: la opción para cambiarlo al inglés.

5.19. Conclusion

Ya sabemos lo suficiente para trasladar nuestra aplicación de ejemplo a Primefaces. Solo hemos visto una quincena de componentes, cuando la biblioteca cuenta con más de 100. Se invita al lector a buscar el componente que le falte directamente en la página web de Primefaces.

5.20. Pruebas con Eclipse

Los proyectos Maven están disponibles en la página web de ejemplos [1]:

Una vez importados a Eclipse, se pueden ejecutar [2]. Se selecciona Tomcat en [3]. A continuación, se muestran en el navegador interno de Eclipse [3].