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 como la analizamos al inicio de este documento:
![]() |
Las páginas JSF se construyeron con tres bibliotecas de etiquetas:
- línea 2: las etiquetas <h:x> del espacio de nombres [http://java.sun.com/jsf/html] que corresponden a las etiquetas HTML,
- línea 3: las etiquetas <f:y> del espacio de nombres [http://java.sun.com/jsf/core] que corresponden a las etiquetas JSF,
- línea 4: las etiquetas <ui:z> del espacio de nombres [http://java.sun.com/jsf/facelets] que corresponden a las etiquetas de los facelets.
Para crear las páginas JSF, vamos a agregar una cuarta biblioteca de etiquetas: las de los componentes PrimeFaces.
- 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 observará. Por lo tanto, aparece en las vistas. Los controladores 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 fáciles de usar 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, se habla de interfaces ricas o RIA (Rich Internet Application).
La arquitectura JSF anterior se convertirá en la siguiente arquitectura PF (PrimeFaces):
![]() |
5.2. Las aportaciones de Primefaces
El sitio 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 siguientes ejemplos, 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:
![]() | ![]() | ![]() |
Solo utilizaremos unos quince de ellos en nuestros ejemplos, pero será suficiente para comprender los principios de construcción de una página de PrimeFaces.
5.3. Aprender a usar Primefaces
Primefaces ofrece ejemplos de uso de cada uno de sus componentes. Solo hay que hacer clic en el enlace correspondiente. Veamos un ejemplo:
![]() |
- en [1], el ejemplo para el componente [Spinner],
- en [2], el cuadro de diálogo que aparece al hacer clic en el botón [Submit].
Aquí encontramos tres novedades:
- el componente [Spinner], que no existe de manera predeterminada en JSF,
- lo mismo ocurre con el cuadro de diálogo,
- y, por último, el POST, que es invocado por el [Submit], está implementado con AJAX. Si observamos detenidamente el navegador durante el POST, no vemos el reloj de arena. La página no se recarga. Simplemente se modifica: un nuevo componente, en este caso el cuadro de diálogo, aparece en la página.
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 encontramos etiquetas JSF clásicas: <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 enriquecidas: <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 a 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> agrega un comportamiento AJAX al spinner. Cada vez que este cambia de valor, se realiza un POST con ese 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 esta manera, el campo [3] sigue las entradas del campo [2].
Aquí tenemos 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 que se denomina DOM (Modelo de Objetos de Documento),
- el servidor aloja la aplicación web que generó esta página,
- en [1], ocurre un evento en la página. Por ejemplo, el incremento de spinner. Este evento es manejado por JavaScript,
- En [2], el JavaScript realiza un POST en la aplicación web. Lo hace de manera asíncrona (la A de AJAX). El usuario puede seguir trabajando con la página. No se congela, pero se puede congelar 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 en notación de objetos),
- en [4], el JavaScript utiliza esta respuesta para actualizar un área específica del DOM, en este caso el área de id=ajaxspinnervalue.
Cuando se utiliza JSF y PrimeFaces, el JavaScript es generado por 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 es la base de PrimeFaces.
Volvamos a nuestro ejemplo y veamos ahora el POST del botón [Submit]:
![]() |
El código asociado al 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 cuentan con 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 regresar el 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 ocurre 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 experimentar para ver qué hacen.
Hemos hablado del modelo, pero aún no lo hemos presentado. Es este:
En general, se puede proceder de la siguiente manera:
- identificar el componente de PrimeFaces que queremos usar,
- estudiar su ejemplo. Los ejemplos de Primefaces están bien hechos 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 agrega 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 agregado 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 a 30, observaremos el repositorio de Maven para Primefaces. Una vez realizadas estas modificaciones, compilamos el proyecto para iniciar la descarga de las dependencias. Así 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 olvidemos la línea 5, que declara el espacio de nombres de la biblioteca de etiquetas de PrimeFaces. Agregamos al proyecto el bean que sirve de modelo 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;
// getters y setters
...
}
La clase es un bean (línea 6) de alcance de consulta (línea 7). Como no se ha especificado un nombre, el bean lleva el nombre de la clase con la primera letra en minúscula: spinnerController.
Al ejecutar el proyecto, se obtiene lo siguiente:
![]() |
Así, acabamos de mostrar cómo probar un ejemplo tomado del sitio web de Primefaces. Todos los ejemplos se pueden probar de esta manera.
A continuación, nos centraremos únicamente en ciertos componentes de Primefaces. Primero retomaremos los ejemplos estudiados con JSF y reemplazaremos ciertas 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 siguientes, nos limitaremos a presentar el código de las páginas y las capturas de pantalla correspondientes. Invitamos 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] (párrafo 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 reemplazado por etiquetas <p:commandLink>. Esta etiqueta tiene un comportamiento predeterminado de 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: diseño de página con facelets
Este proyecto muestra la creación de páginas XHTML utilizando las plantillas de facelets del ejemplo [mv-jsf2-09] (párrafo 2.11):
![]() |
El proyecto de NetBeans es el siguiente:
![]() |
- en [1], los archivos de configuración del proyecto JSF,
- en [2], las páginas 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 a 30. Ahí es donde se insertará la parte variable de la página:
![]() |
- el área enmarcada arriba se actualizará mediante llamadas a AJAX. Para identificarla, la hemos incluido en un contenedor de PrimeFaces generado por la etiqueta <p:outputPanel> (línea 27). Y a este contenedor se le ha asignado el nombre «contenido» (atributo id). Como se encuentra dentro de un formulario, que a su vez es un contenedor con el nombre «formulario», el nombre completo del área dinámica 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, posteriormente, 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 revisar el código fuente de la página HTML recibida:
En el ejemplo anterior, se observa que la etiqueta <h:outputPanel> generó una etiqueta HTML <span>. En este ejemplo, el nombre relativo formulario:contenido (sin el : inicial) y el nombre completo :formulario:contenido (con el : inicial) se refieren al mismo objeto.
Cabe destacar que las llamadas AJAX (<p:commandButton>, <p:commandLink>) que actualizan el área 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 de ID de 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 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 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 generan llamadas a AJAX (ajax=true). Este es el valor predeterminado. Por lo tanto, no es necesario incluir el atributo ajax=true. No lo haremos más adelante. Cabe señalar que estos dos enlaces actualizan el área :formulario:contenido (atributo update), la que aparece enmarcada arriba,
- línea 15: un enlace de navegación AJAX que, una vez más, actualiza el área :formulario:contenido,
- línea 16: se utiliza la etiqueta <h:setPropertyActionListener> para incluir el atributo page2 en la consulta con el valor true. Esto tendrá como efecto mostrar el fragmento [page2.xhtml] (línea 6 a continuación) 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,
- el área dinámica se identificará con el id:formulario:contenido y se actualizará mediante llamadas a AJAX.
5.7. Ejemplo mv-pf-04: formulario de entrada
Este proyecto es la adaptación del proyecto JSF2 [mv-jsf2-03] (véase el párrafo 2.5):
![]() |
El proyecto de NetBeans es el siguiente:
![]() |
Arriba, en [1], se muestran las páginas XHTML del proyecto. El diseño está a cargo de 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 han sido reemplazadas 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>
Esto da como resultado lo siguiente:
Otra forma 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 a 12 definen el encabezado de la tabla:
Las líneas 14 a 25 definen una fila de la tabla:
Las líneas 27 a 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 retroalimentación sobre la calidad de la contraseña.
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 ingresado [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. Sin embargo, hay que tener en cuenta que durante el proceso POST AJAX, se envían todos los valores del formulario. Por lo tanto, se actualiza el modelo completo. Con este modelo, sin embargo, 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 párrafo 2.6):

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 el sitio web del documento.
5.9. Ejemplo: mv-pf-06: navegación – sesión – manejo de excepciones
Este proyecto es la adaptación del proyecto JSF2 [mv-jsf2-05] (véase el párrafo 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íjese 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 ingresados
Este proyecto es la adaptación de los proyectos JSF2 y [mv-jsf2-06] (véase el párrafo 2.8):

La aplicación introduce dos nuevas etiquetas, la etiqueta <p:messages>:
<p:messages globalOnly="true"/>

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 presenta los siguientes cambios:
- el aspecto del mensaje de error es diferente en [1],
- el campo de entrada con error está rodeado por un marco 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 párrafo 2.9):

El proyecto JSF introdujo el concepto de listeners. La gestión de listener con PrimeFaces se realizó de manera diferente.
Con JSF:
Con Primefaces:
- línea 2: la etiqueta <h:selectOneMenu> sin el atributo valueChangeListener,
- línea 4: la etiqueta <p:ajax> agrega un comportamiento AJAX a su etiqueta padre <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. Aquí no es necesario. Simplemente queremos modificar el modelo mediante POST con los valores ingresados.
5.12. Ejemplo: mv-pf-09: entrada asistida
Este proyecto presenta campos de entrada específicos de PrimeFaces que facilitan la entrada de ciertos 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,
- el modelo [Form.java] de esta última.
5.12.2. El modelo
El formulario presenta cuatro campos de entrada asociados al siguiente modelo:
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 a 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 -->
...
<!-- autocompletar -->
...
</p:panelGrid>
</ui:define>
</ui:composition>
</html>
Analicemos los cuatro campos de entrada.
5.12.4. El calendario
La etiqueta <p:calendar> permite seleccionar una fecha de 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, se muestra un calendario:
![]() |
5.12.5. El control deslizante
La etiqueta <p:slider> permite ingresar 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 ingresar el número entero. Este también se puede ingresar 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 la oportunidad 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 ingresar un número entero entre 1 y 12. Se puede ingresar el número directamente en el campo de entrada del spinner o bien usar las flechas para aumentar o disminuir el número ingresado.
5.12.7. La entrada asistida
La entrada asistida consiste en escribir los primeros caracteres de lo que se desea ingresar. 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 con 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 generar sugerencias que correspondan a los caracteres ingresados 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 que el usuario ha escrito en el campo de entrada. Devuelve una lista de sugerencias,
- líneas 4-6: se construye una lista de 10 propuestas que incluye los caracteres recibidos como parámetros y les agrega 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 validamos el siguiente formulario [1]:
![]() |
- en [2], la etiqueta <p:growl> mostrará entonces los mensajes de error asociados a los datos ingresados incorrectamente.
5.13. Ejemplo: mv-pf-10: dataTable - 1
Este proyecto presenta la etiqueta <p:dataTable>, que sirve para mostrar listas de datos

5.13.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,
- 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. El modelo
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;
// constructores
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
...
}
El modelo 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 debe 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 se muestra 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 utilizó 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 por las etiquetas <setPropertyActionListener> se configuran antes de que se ejecute el método designado por el atributo «action», y que este no es el caso del atributo actionListener. En resumen, tan pronto como haya parámetros que enviar a la acción llamada, se debe 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 se necesita 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. Aquí, 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 manera única. Aquí, 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 contamos con los detalles del método utilizado. Podemos suponer que la colección se recorre secuencialmente en busca del elemento que corresponda al rowkey seleccionado. Esto significa que, si el método que asocia rowkey con selection es más complejo, entonces 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:

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` ahora se establece a través de una clase. El atributo value de la línea 5 ahora tiene 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 esta manera, se agrega 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 encontró 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. Aquí, nuestro bean extiende la clase ListDataModel,
- líneas 13-15: el constructor admite como parámetro la lista de personas que administramos. 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 manera ú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. Es decir, en este caso, 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 recibió. Esta referencia se asignará al modelo [Form].personneChoisie. Por lo tanto, el método [retirerPersonne] permanece sin cambios:
public void retirerPersonne() {
// se elimina la persona elegida
personnes.remove(personneChoisie);
}
Esta es la técnica que se debe utilizar cuando el vínculo entre los atributos rowkey y selection no es un simple vínculo de propiedad (rowkey) a objeto (**selection**).
5.16. Ejemplo: mv-pf-13: dataTable - 4
Este proyecto es similar al anterior, salvo que cambia la forma de seleccionar a la persona que se va a retirar:

En el ejemplo anterior, vemos que el objeto se selecciona mediante un menú contextual (clic derecho). Se solicita una confirmación de la eliminación:
![]() |
El formulario [index.xhtml] cambia 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 en 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 ícono [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 completado 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 recibir una respuesta del servidor después de ejecutar una llamada AJAX. Para ello, se utiliza el atributo oncomplete de la llamada AJAX:
![]() |
El formulario [index.xhtml] se modifica 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 de JavaScript de las líneas 13 a 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 verifica 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, se elimina a la persona seleccionada por el usuario de la lista de personas,
- línea 9: de lo 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 agregar al diccionario args, del que ya hablamos, el atributo llamado «msgErreur» con el valor msgErreur generado en la línea 9. Este atributo es recuperado posteriormente 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 a continuación de [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 a la derecha.
Los componentes 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 siguiente vista:
![]() |
- línea 2: muestra [1],
- línea 3: muestra un espacio de 30 píxeles [2],
- líneas 4-6: muestran una lista 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 ícono. Estos íconos son los de JQuery y UI. Su lista se encuentra en URL, [http://jqueryui.com/themeroller/] y [6]:
![]() |
- Para conocer el nombre de un ícono, basta con colocar el cursor sobre él. Luego, 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 este nombre en el atributo icon,
- línea 9: crea una burbuja de ayuda para el botón (atributo «for»). Al colocar el cursor sobre el 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 terminado
break;
} else {
// persona siguiente
i++;
}
}
}
// getters y setters
...
}
Los componentes 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 siguiente vista:
![]() |
- 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 implementar nuestra aplicación de ejemplo en PrimeFaces. Solo hemos visto unos quince componentes, mientras que la biblioteca cuenta con más de 100. Invitamos al lector a buscar el componente que le falte directamente en el sitio web de PrimeFaces.
5.20. Pruebas con Eclipse
Los proyectos Maven están disponibles en el sitio web de ejemplos [1]:
![]() |
Una vez importados a Eclipse, se pueden ejecutar [2]. Selecciona Tomcat en [3]. A continuación, se mostrarán en el navegador interno de Eclipse [3].
![]() |



















































