Manual Jsf 2

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 48

Jsf – Enrique Pedraza
Table of Contents
Introducción..........................................................................................................................................2
Primera aplicación de ejemplo.............................................................................................................5
Código de la aplicación......................................................................................................................6
Eventos y oyentes JSF........................................................................................................................10
Implementación de oyentes como métodos de beans.......................................................................11
Eventos Action y navegabilidad.......................................................................................................12
Reglas de Navegación......................................................................................................................13
Componentes y etiquetas JSF.............................................................................................................16
Etiquetas html...................................................................................................................................17
Ejemplos de Uso de Etiquetas Html...................................................................................................26
Código de la aplicación....................................................................................................................28
Ciclo de vida de una petición JSF......................................................................................................33
El atributo immediate.......................................................................................................................36
Ejemplos reales de uso de immediate=”true”.....................................................................................37
Pushback de eventos valueChange...................................................................................................38
Objetos implícitos en una página jsf..................................................................................................39
Validación...........................................................................................................................................40
Conversores........................................................................................................................................41
Ajax y jsf............................................................................................................................................41
Comunicación en Jsf 2........................................................................................................................42
Oyentes de fases.................................................................................................................................42
I18N....................................................................................................................................................42
Misc....................................................................................................................................................44
Managed Beans..................................................................................................................................45
Aplicación de Gestión........................................................................................................................45
Introducción
JSF (Java Server Faces) es un framework de desarrollo basado en el patrón MVC (Modelo Vista
Controlador) orientado al interfaz de usuario. El modelo web estándar de desarrollo con servlets y
jsp se basa en peticiones a url determinadas que son procesadas por servlets que a su vez delegan la
construcción de la vista en páginas jsp, de esta forma:
El procesamiento típico cuando la petición llega al servlet es de recuperación de los parámetros que
envía el cliente, análisis de dichos parámetros, delegación en métodos de la capa de negocio en
función de esos parámetros y reenvío de los resultados a la página jsp para que construya la vista.
Este mecanismo termina resultando tedioso, repetitivo y propenso a errores. Hoy en día existen
frameworks de desarrollo, como Struts, Spring o JSF que automatizan y facilitan el desarrollo de
aplicaciones MVC para entornos web.
Lo que diferencia a JSF de otros frameworks similares es que JSF aspira a recuperar el modelo
tradicional de oyentes y eventos utilizado en aplicaciones monopuesto pero orientado a aplicaciones
web. Para ello cuenta con dos elementos principales:
Un api para representar los componentes de interfaz de usuario y manejar su estado;
gestionar eventos, validar en el servidor y convertir los datos; definir la navegación; soportar
internacionalización y accesibilidad; y proveer extensibilidad para todas estas
características.
Un conjunto de librerías personalizadas de etiquetas para páginas JSP que renderizan los
componentes de usuario en la página y que enlazan dichos componentes a objetos del lado
del servidor.
JSF es una especificación de Sun, liderada por Craig R. McClanahan, quien a su vez es el creador
de Struts. Es por ello que muchas características de Struts se encuentran también en la
especificación de JSF. Al ser una especificación, existen distintas implementaciones proporcionadas
por distintos fabricantes.
Modelo de ejecución:
JSF constituye una vuelta al paradigma evento/oyente característico de las aplicaciones
monopuesto. En una aplicación web clásica la interfaz de usuario se construye a través de etiquetas
HTML de esta forma:
<form action=”servlet” method=”post”>
<input type=”text” name=”nombreUsuario”/>
<input type=”submit” value=”aceptar”
...
</form>
Al enviar el formulario, el valor de los campos es recogido por un servlet uno a uno, con código
parecido a éste:
String nombre = request.getParameter(“nombreUsuario”);
Esto puede convertirse en una tarea muy tediosa y muy propensa a errores cuando trabajamos con
formularios muy grandes. Si a esto añadimos las tareas de conversión de los parámetros desde el
tipo original en que son enviados (String), su validación, la presentación de errores, el rellenado de
los formularios en caso de que exista un error en la página... nos encontramos con un sistema en el
que malgastamos mucho tiempo en tareas repetitivas para las que ya existen diversas soluciones.
Entre estas soluciones se encuentra JSF, que se diferencia de las demás en el enfoque evento/oyente
hacia el que está orientado. Cuando trabajamos con JSF no pensamos en los parámetros que envía el
cliente, sino en eventos que se pueden producir en nuestra página y en cómo reaccionar a dichos
eventos.
Una aplicación JSF se compone de:
Una capa de presentación normalmente compuesta por un conjunto de Facelets (páginas
xhtml) junto con un conjunto de etiquetas personalizadas para representar los componentes
en la página.
Componentes (objetos) del lado servidor que representan los componentes gráficos de la
página
Renderers que dibujan estos componentes generando una salida adecuada a la presentación
elegida.
Conversores y validadores que convierten y validan respectivamente los datos que envía el
cliente.
Un conjunto de beans de soporte (Managed Beans) que definen propiedades para almacenar
estos datos y métodos para reaccionar a los eventos de una página.
Un fichero de configuración donde podemos definir las reglas de navegación y configurar
los beans y otros componentes.
Uno o varios ficheros de recursos con los mensajes de la aplicación en distintos idiomas.
JSF proporciona elementos que facilitan el desarrollo de las capas de control y presentación y su
integración con el modelo.
Para este manual hemos utilizado la versión 2.1.9 de la especificación JSF (Mojarra) y Tomcat 8.
Primera aplicación de ejemplo
Para implementar una aplicación JSF básica necesitamos configurar o aportar los siguientes
elementos:
1. Configurar el fichero web.xml para especificar qué peticiones va a procesar el entorno de
JSF.
2. Crear páginas facelet (.xhtml) para mostrar al cliente. Estas páginas contendrán
componentes de usuario, algunos de los cuales lanzarán eventos al ser manipulados.
3. Crear una o varias clases (Managed Beans) que almacenen la información suministrada por
el cliente en las página facelets y que respondan (oyentes) a los posibles eventos que se
produzcan en dichas páginas.
4. Opcionalmente, crear uno o varios ficheros de configuración (faces-config.xml, …) en la que
podemos configurar diferentes elementos de la aplicación, como los Managed Beans, reglas
de navegación, etc.
Vamos a crear una aplicación básica para mostrar el funcionamiento de JSF. La aplicación va a
consistir en una única página facelet con un campo de texto y un botón:
Cuando escribamos algo en el campo de texto y pulsemos el botón, aparecerá un mensaje con el
texto en mayúsculas:
Para mostrar más características del framework obligaremos a que haya algo introducido en el
campo de texto antes de pulsar el botón, y en caso contrario mostraremos un mensaje de validación
estándar; además, el texto que acompaña al texto en mayúsculas permanecerá oculto hasta que
escribamos algo y pulsemos el botón.
Para compilar y desplegar vamos a necesitar las librerías JSF, en este caso estamos utilizando los
ficheros jsf-api-2.1.9.jar y jsf-impl-2.1.9.jar.
Código de la aplicación
Fichero web.xml: En el fichero web.xml configuramos el servlet de JSF para que tramite las
peticiones del cliente a la aplicación, además de configurar algunos parámetros del framework:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>jsf simple</display-name>
<description>jsf simple</description>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<!-- Faces Servlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<!-- Welcome files -->
<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
Como podemos observar primero declaramos un par de parámetros de contexto relativos al
framework JSF, para a continuación declarar el servlet controlador JSF, asociarlo a peticiones con
extensión .xhtml y establecer como página de inicio index.xhtml.
Obviamos en este momento el listado de index.xhtml y mostramos el código de la página facelet
pasaamayuscula.xhtml
pasaamayuscula.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:f="http://java.sun.com/jsf/core">
<h:head>
<title>Jsf Básico</title>
</h:head>
<h:body>
<h1>Aplicación que pasa a Mayúscula</h1>
<p>Cuando escribas algo en el campo de texto y pulses el botón aparecerá un mensaje
mostrando el contenido
del campo de texto en mayúsculas</p>
<h:form id="formmay">
<p>
<h:inputText id="textoOrig" value="#{mayusBean.textoOriginal}"
required="true"/>
<h:commandButton id="btMay" value="Pasar a mayúscula"
actionListener="#{mayusBean.pasaAMayusculas}"/>
<h:messages/>
</p>
<h:panelGroup rendered="#{mayusBean.rendering}">
El texto en mayúsculas es #{mayusBean.textoEnMayusculas}.
</h:panelGroup>
</h:form>
</h:body>
</html>
La página facelet es un fichero xhtml en el que construimos el interfaz de usuario a través de
etiquetas JSF. Analicemos los elementos más significativos de la página:
<html xmlns=...: en el elemento html especificamos los espacios de nombres que vamos a utilizar
en la página facelet. En este caso estamos especificando tres espacios de nombres. El primero es el
espacio de nombres por defecto (sin prefijo) que va a declarar todas las etiquetas xhtml que
podemos usar en la página (html, title, h1, p...). Los otros dos son declaraciones de JSF con los
que indicamos que vamos a utilizar las librerías de etiquetas core (f:) y html (h:).
<h:form id="formmay">: abrimos el formulario que contendrá componentes a procesar en el
servidor. Daos cuenta de que no especificamos atributos method ni action.
<h:inputText id="textoOrig" value="#{mayusBean.textoOriginal}" required="true"/>:
especificamos un <h:inputText para que el framework nos presente un campo en el que introducir
texto. El atributo value utiliza JSF EL para hacer referencia a una propiedad de lectura/escritura
textoOriginal en el Managed Bean mayusBean. De esta manera, al dibujar el campo de texto el
framework le dará el valor que tenga dicha propiedad en ese momento (lectura). Del mismo modo,
cuando el formulario se envíe al servidor el framework se encargará de actualizar dicha propiedad
con el valor contenido en el campo de texto (escritura).
Por otra parte, el atributo required obliga a que el campo contenga algún valor al enviar el
formulario. Si no es así, el framework redibujará la página con los valores enviados y no se
realizará ningún procesamiento de los datos.
<h:commandButton id="btMay" value="Pasar a mayúscula"
actionListener="#{mayusBean.pasaAMayusculas}"/>: esta etiqueta dibuja un botón con el texto
Pasar a mayúscula que cuando se pulsa invoca el método pasaAMayusculas del bean mayusBean.
Este método está definido como un actionListener dentro de la aplicación.
<h:messages/>: con esta etiqueta mostramos al usuario posibles errores de validación.
<h:panelGroup rendered="#{mayusBean.rendering}">: la etiqueta panelGroup se usa para agrupar
componentes en una página. En este caso la usamos combinada con el atributo rendered, que
dibuja o no los componentes anidados en el panelGroup en función de la propiedad rendering de
mayusBean.
#{mayusBean.textoEnMayusculas}: a partir de la versión 2.0 de JSF podemos utilizar EL para
imprimir los atributos de un bean directamente sin necesidad de utilizar la etiqueta outputLabel.
En este caso estamos mostrando el valor del atributo textoEnMayusculas del bean mayusBean.
MayusBean.java
@ManagedBean(name="mayusBean") // En este caso el atributo name es redundante
@RequestScoped
public class MayusBean {
private String textoOriginal;
private String textoEnMayusculas;
private boolean rendering;
public String getTextoOriginal() {
return textoOriginal;
}
public void setTextoOriginal(String textoOriginal) {
this.textoOriginal = textoOriginal;
}
public String getTextoEnMayusculas() {
return textoEnMayusculas;
}
public void setTextoEnMayusculas(String textoEnMayusculas) {
this.textoEnMayusculas = textoEnMayusculas;
}
public boolean isRendering() {
return rendering;
}
public void setRendering(boolean rendering) {
this.rendering = rendering;
}
public void pasaAMayusculas(ActionEvent event) {
setRendering(true);
setTextoEnMayusculas(getTextoOriginal().toUpperCase());
}
}
MayusBean es la clase java que va a servir de enlace entre la información y los eventos de la parte
cliente y el servidor, y es lo que en terminología JSF conocemos como Managed Bean. Los elementos
más significativos de esta clase son:
@ManagedBean y @RequestScoped son anotaciones que registran un Managed Bean con la aplicación y
le dan un scope de request, respectivamente. El uso de anotaciones es estándar desde la versión 2
de JSF. También sería posible prescindir de las anotaciones y declarar el bean en el fichero de
configuración faces-config.xml, como se hacía en versiones anteriores del framework.
El atributo name de @ManagedBean asigna el alias con el que va a ser conocido el bean dentro de la
aplicación. Si no se declara un atributo name se asigna un alias automático equivalente al nombre
de la clase comenzando en minúsculas.
Las propiedades textoOriginal y textoEnMayusculas representan el valor que el usuario introduce
en el campo de texto y el que se muestra transformado en mayúsculas, respectivamente. La
propiedad rendering se usa para determinar si este último valor se muestra o no.
El método pasaAMayusculas(ActionEvent event), que en la página facelet está definido como un
actionListener es invocado cuando se pulsa el botón dibujado en la página. Su función es
modificar la propiedades rendering y textoEnMayusculas para que el mensaje en mayúsculas sea
mostrado cuando se renderice la respuesta.
Eventos y oyentes JSF
La especificación JavaServer Faces define tres tipos de eventos: eventos de aplicación, eventos de
sistema y eventos de modelado de datos.
Los eventos de aplicación son locales a una aplicación y son generados por componentes de la
página. Un objeto Event identifica y almacena información sobre el componente que generó el
evento. La aplicación proporciona métodos de respuesta para los eventos que quiera tratar y los
asocia a los componentes que generan dichos eventos. Cuando el usuario interacciona con la UI, por
ejemplo pulsando un botón, se lanza un evento. Este hecho causa que la implementación JavaServer
Faces invoque el método oyente que procesa ese evento.
JavaServer Faces trabaja con dos tipos de eventos de aplicación: eventos de acción y eventos de
cambio de valor.
Un evento de acción (javax.faces.event.ActionEvent) se lanza cuando el usuario activa un
componente que implementa el interfaz ActionSource. Estos componentes incluyen botones y
enlaces.
Un evento de cambio de valor (javax.faces.event.ValueChangeEvent) sucede cuando el usuario
modifica el valor de un componente representado por UIInput o alguna de sus subclases. Un
ejemplo sería al seleccionar un check box, acción que modifica el valor del componente a true. Los
tipos de componente que generan este tipo de eventos son UIInput, UISelectOne, UISelectMany y
UISelectBoolean.
Los eventos de cambio de valor se lanzan únicamente si no se producen errores de validación.
Dependiendo de si el valor de la propiedad immediate del componente que lanza el evento es true o
false, los eventos de acción pueden ser procesados durante las fases Invoke Application o Apply
Request Values, y los eventos de cambio de valor durante las fases Process Validations o Apply
Request Values (ver más adelante la sección sobre el ciclo de vida de una página JSF). Si el valor
de esta propiedad es true, en ambos casos los eventos serían procesados durante la fase Apply
Request Values (por defecto es false).
Un evento de sistema es generado por un Object, y no por un componente. Son generados en
momentos predefinidos durante la ejecución de la aplicación.
Un evento de modelado de datos sucede cuando una nueva fila de un componente UIData es
seleccionada.
Hay dos formas de configurar una aplicación para que reaccione a eventos de aplicación:
Implementar un método de un managed bean para que gestione el evento y utilizar EL para
referirnos a él desde el atributo apropiado de la etiqueta del componente.
Implementar una clase event listener que maneje el evento y registrar este oyente con el
componente, anidando una etiqueta f:valueChangeListener o f:actionListener en la
etiqueta del componente.
Implementación de oyentes como métodos de beans
Para implementar un oyente que reaccione a un evento como método de un bean, necesitamos
declarar en el elemento que lanza el evento un atributo actionListener o valueChangeListener que
enlace con un método de un bean que responda a ese evento.
Un ejemplo de action listener por método podría ser el siguiente:
- facelet:
<h:commandButton id="btMay" value="Pasar a mayúscula"
actionListener="#{mayusBean.pasaAMayusculas}"/>
- managed bean:
public void pasaAMayusculas(ActionEvent event) {
setRendering(true);
setTextoEnMayusculas(getTextoOriginal().toUpperCase());
}
Como ya sabemos, un método actionListener debe devolver void y recibir un objeto
javax.faces.event.ActionEvent. Este objeto Event proporciona información sobre el evento, por
ejemplo, la fase en la que se ha lanzado el evento o el componente que lo lanzó.
Veamos ahora un ejemplo de valueChangeListener como método de bean:
- facelet:
<h:selectOneRadio layout="pageDirection"
value="#{exCmBean.radioBtSelected}" onchange="submit()"
valueChangeListener="#{exCmBean.nuevoRBSeleccionado}">
<f:selectItems value="#{exCmBean.opcionesAMostrarEnRB}" />
</h:selectOneRadio>
- managed bean:
public void nuevoRBSeleccionado(ValueChangeEvent event) {
int actualValue = (Integer) event.getNewValue();
switch (actualValue) {
case 0: mensajeFavorito = "Mi color favorito es el ¡rojo!";
break;
case 15: mensajeFavorito = "Mi canción favorita es Nutshell";
break;
case 22: mensajeFavorito = "Mi libro preferido es... no sé... hay tantos...";
break;
}
}
En este ejemplo además de los atributos value y valueChangeListener declaramos layout, para que
los radio button se muestren en una columna en vez de en una fila, y onchange, para que al
seleccionar un radio button nuevo se envíe el formulario. Por otra parte el método oyente hace uso
del objeto event recibido para recuperar el nuevo valor y actuar en consecuencia. No podríamos
consultar directamente la propiedad asociada al componente porque, según el ciclo de fases de una
petición JSF (ver capítulo correspondiente), en el momento en que se procesan los eventos de
cambio de valor las propiedades del modelo (bean) aún no están actualizadas, por lo que la
propiedad asociada (radioBtSelected) aún contendría el valor antiguo.
Es posible modificar este comportamiento configurando el método oyente para procesar el evento
en una fase más avanzada y así asegurarnos que la propiedad ya se encuentra actualizada. Esta
técnica la analizaremos en el capítulo dedicado al ciclo de vida de una página JSF.
Eventos Action y navegabilidad
Un evento action se produce al pulsar un botón o un hiperenlace. Además de responder a dicho
evento, la aplicación puede elegir entre navegar a otra página o permanecer en la página original.
Ya hemos visto un ejemplo de implementación de un ActionListener en nuestra primera aplicación
de ejemplo, donde definíamos un componente botón que lanzaba un evento al ser pulsado:
<h:commandButton id="btMay" value="Pasar a mayúscula"
actionListener="#{mayusBean.pasaAMayusculas}"/>
este código registra un método oyente con el componente que será invocado cuando se pulse el
botón.
public void pasaAMayusculas(ActionEvent event) {
setRendering(true);
setTextoEnMayusculas(getTextoOriginal().toUpperCase());
}
Éste es el método ActionListener que responde al evento Action. Un método ActionListener debe
ser público, devolver void y recibir un objeto javax.faces.event.ActionEvent, que puede ser usado
para obtener información acerca del evento.
Por otra parte, también podemos declarar métodos Action que respondan a eventos pero que
utilizamos para navegar entre páginas de la aplicación. Un método Action es un método público con
o sin parámetros que devuelve un String. Este String se utiliza para determinar la página a la que
navegaremos cuando se complete el procesamiento de la petición, bien de forma directa (el objeto
String representa el nombre de un recurso físico) o mediante una referencia al modelo de
navegación declarado en los ficheros de configuración de JSF:
Fichero .xhtml:
<h:commandButton immediate="true" value="Volver al índice"
action="#{sesBean.salir}" />
ManagedBean:
public String salir() {
accesosAPagina++;
return "index";
}
En este caso se está lanzando un evento Action que ejecuta el método salir() del bean sesBean.
Este todo realiza una tarea y devuelve la página .xhtml que va a representar el resultado. index
puede ser el nombre real de la página (index.html) o un alias definido en la sección de reglas de
navegación del fichero faces-config.xml. Un ejemplo de evento action con parámetros sería el
siguiente:
<h:form>
<h:dataTable value="#{bean.users}" var="user">
<h:column>#{user.id}</h:column>
<h:column>#{user.name}</h:column>
<h:column>#{user.email}</h:column>
<h:column><h:commandButton value="Edit"
action="#{bean.edit(user)}" /></h:column>
</h:dataTable>
</h:form>
public void edit(User user) {
this.user = user;
this.edit = true;
}
Reglas de Navegación
La navegación entre las diferentes páginas de una aplicación JavaServer Faces, como elegir la
siguiente página a mostrar tras la pulsación de un botón o un hiperenlace, se define mediante una
serie de reglas. Estas reglas de navegación pueden ser implícitas o definidas explícitamente en
nuestro fichero de configuración.
Cada regla de navegación especifica cómo navegar desde una página a otras. La implementación
JavaServer Faces elige la regla de navegación apropiada en función de la página que se muestra en
ese momento.
Tras elegir la regla de navegación apropiada, la elección de la siguiente página depende de dos
factores:
1. El método action invocado por el componente pulsado
2. El resultado lógico referenciado por la etiqueta del componente o devuelto por el método
action
<navigation-rule>
<from-view-id>/login.xhtml</from-view-id>
<navigation-case>
<from-action>#{loginForm.login}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/storefront.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{loginForm.login}</from-action>
<from-outcome>failure</from-outcome>
<to-view-id>/login.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
En esta regla de navegación estamos definiendo dos posibles navegaciones desde login.xhtml. La
primera daría como resultado la página storefront.xhtml si el String devuelto por el método
action #{loginForm.login} es success. La segunda mostraría de nuevo login.xhtml, si el String
devuelto por el método action #{loginForm.login} es failure.
Cada elemento navigation-rule corresponde a una única página definida por el elemento opcional
from-view-id. Esto significa que cada regla define todos los caminos de navegación posibles desde
una página en particular. Si no existe el elemento from-view-id, las reglas de navegación definidas
en ese elemento navigation-rule se aplican a todas las páginas de la aplicación. El elemento from-
view-id permite también el uso de comodines. Por ejemplo, este elemento from-view-id indica que
esta regla de navegación se aplica sobre todas las páginas del directorio books:
<from-view-id>/books/*</from-view-id>
Un elemento navigation-rule puede contener cero o más elementos navigation-case. El elemento
navigation-case define un conjunto de de criterios de coincidencia. Cuando estos criterios son
satisfechos la aplicación navegará a la página definida por el elemento to-view-id.
Los criterios de navegación se definen mediante elementos opcionales from-outcome y from-action.
El elemento from-outcome define un resultado lógico, como por ejemplo success. El elemento
from-action utiliza una expresión de método para referirse a un método action que devuelve un
String, que es a su vez el resultado lógico.
Los elementos navigation-case se comprueban contra la salida y la expresión de método en el
siguiente orden:
1. Si especificamos tanto un valor from-outcome como un valor from-action, la regla se
cumple siempre que el oyente ejecutado sea el método especificado en el elemento from-
action y el resultado lógico del método sea el valor especificado en el elemento from-
outcome.
2. Si sólo se especifica un valor from-outcome, la regla se cumple si este valor coincide con el
resultado lógico devuelto por el action que se invoque (ya sea el resultado de un método o
el valor del atributo action del componente).
3. Si sólo se especifica un valor from-action, la regla se cumple si el oyente action es el
método especificado.
Cuando la regla se cumple, la página dibujada es la definida por el elemento to-view-id.
Componentes y etiquetas JSF
Cuando construimos una página JSF utilizamos una serie de etiquetas que al ser procesadas por el
servidor generan un árbol de componentes que representan cada uno de los objetos definidos en la
página. Cada etiqueta JSF está asociada a un componente + un renderer que dibuja el componente
en la página resultado. Al separar el componente de su renderer permitimos al usuario personalizar
el dibujado de los componentes, o utilizar el mismo componente con diferentes renderers para
distintas etiquetas (por ejemplo, el componente UICommand está asociado a las etiquetas
h:commandButton y h:commandLink, y sus renderers respectivos dibujan un botón o un hiperenlace,
respectivamente).
Los componentes estándar existentes en la arquitectura son los siguientes:
UIColumn: Representa una columna de datos de un compontente UIData.
UICommand: Representa un control que lanza eventos action.
UIData: Representa un enlace de datos a una colección de datos representada por una
instancia javax.faces.model.DataModel.
UIForm: Representa un formulario que se presenta al usuario. Sus componentes hijas
representan (entre otras cosas) los campos de entrada a incluir cuando el formulario se
envía. Este componente es análogo a la etiqueta form en HTML.
UIGraphic: Muestra una imagen.
UIInput: Componente que recibe datos introducidos por el usuario (por ejemplo desde un
campo de texto). Es una subclase de UIOutput.
UIMessage: Muestra un mensaje de error en función del idioma del cliente.
UIMessages: Muestra un conjunto de mensajes de error en función del idioma del cliente.
UIOutcomeTarget: Muestra un hiperenlace bien como un hiperenlace o como un botón.
UIOutput: Muestra datos en la página.
UIPanel: Gestiona la apariencia de sus componentes hijos.
UIParameter: Representa parámetros de substitución.
UISelectBoolean: Permite al usuario establecer un valor booleano en un control
seleccionando o deseleccionando éste. Esta clase es hija de UIInput.
UISelectItem: Representa un elemento de un conjunto de elementos (por ejemplo en una
lista desplegable).
UISelectItems: Representa un conjunto completo de elementos.
UISelectMany: Permite a un usuario realizar selecciones múltiples de entre un conjunto de
elementos. Es hija de UIInput.
UISelectOne: Permite a un usuario seleccionar un elemento de entre un conjunto de
elementos. Es hija de UIInput.
UIViewParameter: Representa los parámetros de una request. Es hija de UIInput.
UIViewRoot: Representa la raíz del árbol de componentes.
Estos componentes, como ya se ha mencionado, están asociados a una o más de las etiquetas JSF
que utilizamos para construir páginas. Por tanto, una etiqueta se transforma en un componente en el
servidor que a su vez se dibuja en la salida utilizando un renderer.
Etiquetas html
Las librerías de etiquetas html y core definen las etiquetas JSF con las que construimos una página.
Las etiquetas html representan componentes de un formulario y otros componentes básicos,
mientras que las etiquetas core sirven de apoyo a la librería html.
En la siguiente tabla se resumen las diferentes etiquetas que conforman la librería html antes de
analizar cada etiqueta por separado:
Etiqueta Función Dibujada como Apariencia
h:column Representa una columna de datos en un
componente de datos
Una columna de datos en una tabla
HTML
Una columna en una tabla
h:commandButton Envía un formulario al servidor Un elemento HTML <input type=valor>
en el que valor puede ser "submit","reset",
o "image"
Un botón
h:commandLink Enlaza a otra página Un elemento HTML <a href> Un hiperenlace
h:dataTable Encapsula un conjunto de datos Un elemento HTML <table> Una tabla que puede ser
construida dinámicamente
h:form Representa un formulario Un elemento HTML <form> Sin apariencia
h:graphicImage Muestra una imagen Un elemento HTML <img> Una imagen
h:inputFile Permite la subida de ficheros Un elemento HTML <input type="file"> Un campo de texto con un
botón Browse...
h:inputHidden Permite incluir un campo oculto en un
formulario
Un elemento HTML <input
type="hidden">
Sin apariencia
h:inputSecret Permite al usuario incluir un campo de texto sin
que el texto aparezca al teclearlo
Un elemento HTML <input
type="password">
Campo de texto que muestra
una fila de asteriscos en lugar
del texto correspondiente
h:inputText Permite al usuario incluir un campo de texto Un elemento HTML <input type="text"> Un campo de texto
h:inputTextarea Permite al usuario incluir un campo de texto
multilínea
Un elemento HTML <textarea> Campo de texto multilínea
h:message Muestra un mensaje en función del idioma Un elemento HTML <span> si se usan
estilos
Un texto
h:messages Muestra un conjunto de mensajes en función
del idioma
Un conjunto de elementos HTML <span>
si se usan estilos
Un texto
h:outputFormat Muestra un mensaje formateado Texto plano Texto plano
h:outputLabel Muestra un componente anidado como etiqueta
de un campo de entrada específico
Un elemento HTML label> Texto plano
h:outputLink Enlaza a otra página sin generar un evento de
acción
Un elemento HTML <a> Un hiperenlace
h:outputText Muestra una línea de texto Texto plano Texto plano
h:panelGrid Muestra una tabla Un elemento HTML <table> con
elementos <tr> y <td>
Una tabla
h:panelGroup Agrupa un conjunto de componentes como un
único hijo de un elemento padre
Un elemento HTML <div> o <span> Una fila de una tabla
h:selectBooleanCheckbox Permite a un usuario modificar el valor de un Un elemento HTML <input Un check box
elemento booleano type="checkbox">
h:selectManyCheckbox Muestra un conjunto de check boxes para que el
usuario seleccione múltiples valores
Un conjunto de elementos HTML
<input> de tipo checkbox
Un conjunto de check boxes
h:selectManyListbox Permite a un usuario elegir múltiples valores de
un conjunto de elementos que se muestran
como una lista
Un elemento HTML <select> Una lista
h:selectManyMenu Permite a un usuario elegir múltiples valores de
un conjunto de elementos que se muestran
como una menú desplegable
Un elemento HTML <select> Un menú desplegable
h:selectOneListbox Permite a un usuario elegir un valor de un
conjunto de elementos que se muestran como
una lista
Un elemento HTML <select> Una lista
h:selectOneMenu Permite a un usuario elegir un valor de un
conjunto de elementos que se muestran como
una menú desplegable
Un elemento HTML <select> Un menú desplegable
h:selectOneRadio Permite a un usuario elegir un valor de un
conjunto de elementos que se muestran como
radio buttons
Un conjunto de elementos HTML <input
type="radio">
Un conjunto de opciones
- h:inputText:
<h:inputText id="textoOrig" size=”20” value="#{mayusBean.textoOriginal}"
required="true" requiredMessage="#{bundle.reqTextoOr}" >
<f:valueChangeListener
type="profe.jsfbasic.web.listeners.textoOrListener" />
</h:inputText>
En este código estamos dibujando un campo de texto de rellenado obligatorio con un tamaño de 20
caracteres. Además referenciamos un mensaje de error en un fichero de recursos en caso de que no
se introduzca ningún texto, y asociamos un oyente de cambio de valor usando la etiqueta core
f:valueChangeListener. Como veremos más adelante, el oyente puede ser o bien un método dentro
de un bean o, como en este caso, una clase que implemente el interfaz ValueChangeListener.
- h:inputSecret:
<h:inputSecret redisplay="false"
value="#{loginBean.password}" />
Enlazamos el texto que introduzca el usuario con la propiedad password del bean loginBean. El
atributo redisplay indica si se vuelve a mostrar el contenido del campo en caso de redibujar la
página (normalmente debido a errores de validación de algún componente).
- h:outputLabel:
<h:selectBooleanCheckbox id="fanClub"
rendered="false"
binding="#{cashierBean.specialOffer}" />
<h:outputLabel for="fanClub"
rendered="false”
binding="#{cashierBean.specialOfferText}">
<h:outputText id="fanClubLabel"
value="#{bundle.DukeFanClub}" />
</h:outputLabel>
En este caso estamos asociando una etiqueta al check box representado por el elemento con id
fanClub. El valor de la etiqueta viene dado por la clave DukeFanClub dentro del fichero de recursos.
Es necesario hacer notar que, en este ejemplo, estamos utilizando el atributo binding en los
elementos selectBooleanCheckbox y outputLabel para asociar dichos componentes a objetos reales
declarados en el lado servidor; es decir, la propiedad cashierBean.specialOffer es del tipo
javax.faces.component.html.HtmlSelectBooleanCheckbox, y la propiedad specialOfferText del
bean cashierBean es un objeto de tipo javax.faces.component.html.HtmlOutputLabel. JSF se
encarga de asignar el componente real a la propiedad para permitirnos un mayor control del
componente, en caso de que sea necesario.
La etiqueta h:outputText es, en este caso concreto, innecesaria, ya que podríamos haber asignado el
valor de dicha etiqueta directamente al elemento h:outputLabel de esta forma:
<h:outputLabel for="fanClub"
rendered="false"
binding="#{cashierBean.specialOfferText}"
value="#{bundle.DukeFanClub}" />
- h:outputLink: La etiqueta h:outputLink se utiliza para dibujar un hiperenlace que enlaza con otra
página pero no genera un evento de acción. Si quisiéramos realizar algún procesamiento con la
pulsación de un hiperenlace utilizaríamos la etiqueta h:commandLink.
<h:outputLink value="javadocs">
Documentation for this demo
</h:outputLink>
- h:outputFormat: Esta etiqueta nos permite mostrar mensajes concatenados tal y como se describe
en la documentación de java.text.MessageFormat:
<h:outputFormat value="Hello, {0}!">
<f:param value="#{hello.name}"/>
</h:outputFormat>
<h:outputFormat value="Hello, {0}! You are visitor number {1} to the page.">
<f:param value="#{hello.name}" />
<f:param value="#{bean.numVisitor}"/>
</h:outputFormat>
- h:commandButton y h:commandLink: Estas dos etiquetas, dibujadas como un botón y un
hiperenlace respectivamente, nos permiten realizar una acción y navegar a otra página al mismo
tiempo. Para ello utilizamos el atributo action, cuyo valor debe ser el alias de la página a la que
queremos navegar. También podemos asociar un actionListener que realice alguna acción sin
navegación asociada:
<h:commandButton value="Submit"
action="#{cashierBean.submit}"/>
<h:commandLink id="Duke" action="bookstore">
<f:actionListener
type="javaeetutorial.dukesbookstore.listeners.LinkBookChangeListener" />
<h:outputText value="#{bundle.Book201}"/>
/h:commandLink>
En el primer caso el atributo action hace referencia al método submit del bean cashierBean, que
realizará un procesamiento determinado y devolverá un string que será tomado como indicativo de a
qué página debemos navegar; en el segundo, tenemos un atributo action que indica directamente la
página resultante, y un listener representado por un objeto de la clase LinkBookChangeListener,
objeto que debe implementar el interfaz javax.faces.event.ActionListener.
- h:graphicImage:
<h:graphicImage id="mapImage" url="/resources/images/book_all.jpg"
usemap="#bookMap"/>
- h:panelGrid y h:panelGroup: Un panel en JSF se utiliza para agrupar elementos que son
dibujados como celdas de una tabla. h:panelGrid se utiliza para dibujar tablas reales HTML;
h:panelGroup agrupa otros elementos en una única celda o, utilizado fuera de un h:panelGrid,
agrupa elementos y texto plano (por ejemplo, con un atributo rendered común que muestra u oculta
dichos elementos, como vimos en el ejemplo inicial).
<h:panelGrid columns="2"
headerClass="list-header"
styleClass="list-background"
rowClasses="list-row-even, list-row-odd"
summary="#{bundle.CustomerInfo}"
title="#{bundle.Checkout}">
<f:facet name="header">
<h:outputText value="#{bundle.Checkout}"/>
</f:facet>
<h:outputLabel for="name" value="#{bundle.Name}" />
<h:inputText id="name" size="30"
value="#{cashierBean.name}"
required="true"
requiredMessage="#{bundle.ReqCustomerName}">
<f:valueChangeListener
type="javaeetutorial.dukesbookstore.listeners.NameChanged" />
</h:inputText>
<h:message styleClass="error-message" for="name"/>
<h:outputLabel for="ccno" value="#{bundle.CCNumber}"/>
<h:inputText id="ccno"
size="19"
converterMessage="#{bundle.CreditMessage}"
required="true"
requiredMessage="#{bundle.ReqCreditCard}">
<f:converter converterId="ccno"/>
<f:validateRegex
pattern="\d{16}|\d{4} \d{4} \d{4} \d{4}|\d{4}-\d{4}-\d{4}-\d{4}" />
</h:inputText>
<h:message styleClass="error-message" for="ccno"/>
...
</h:panelGrid>
Este panelGrid define una tabla con dos columnas y n filas. Las filas son determinadas por el
número de componentes anidados en el panelGrid, el primer componente va a la primera celda, el
segundo a la segunda, el tercero a la primera celda de la segunda fila, etc. Si especificamos un
facelet con name header, o footer, o caption, se genera un elemento th expandido al número de
columnas de la tabla. Si existe un facelet header debe estar definido como primer elemento, si
existe un facelet footer debe estar definido como último elemento. Si necesitamos celdas
diferentes como header tendremos que recurrir a estilos (atributos style o styleClass) en los
elementos correspondientes.
Un elemento facelet sólo puede tener un elemento hijo por lo que si necesitamos anidar más de un
elemento dentro de un facelet los agruparíamos dentro de un h:panelGroup. De la misma forma,
también utilizaríamos h:panelGroup para agrupar varios elementos JSF dentro de una misma celda.
También vemos en este ejemplo la referencia a diferentes listeners, conversores y validadores,
además de la utilización de la etiqueta h:message asociada al elemento con id ccno como un
elemento más del panelGrid.
Otro ejemplo más sencillo:
<h:panelGrid columns="3">
Enter a number :
<h:inputText id="number" value="#{dummy.number}"
size="20" required="true"
label="Number" >
<f:convertNumber />
</h:inputText>
<h:message for="number" style="color:red" />
</h:panelGrid>
Aquí definimos un panelGrid con tres columnas (y, por el número de elementos, una única fila); la
primera celda contendrá el texto Enter a number; la segunda, un campo de texto, y la tercera, el
posible mensaje de error.
- h:selectBooleanCheckbox: Utilizamos esta etiqueta para mostrar un checkbox.
<h:selectBooleanCheckbox id="fanClub"
rendered="false"
binding="#{cashierBean.specialOffer}" />
<h:outputLabel for="fanClub"
rendered="false"
binding="#{cashierBean.specialOfferText}"
value="#{bundle.DukeFanClub}" />
Este ejemplo muestra la utilización del atributo binding; en este caso lo utilizamos, además de para
acceder al valor del elemento checkbox (para lo cuál podríamos haber utilizado simplemente una
propiedad del bean y asociarla al elemento mediante el atributo value), para poder acceder a los
componentes asociados a las etiquetas en el lado servidor, y modificar el atributo rendered de forma
programática.
- h:selectOneRadio, h:selectOneMenu y h:selectOneListbox: Estas etiquetas muestran un
conjunto de radio buttons, un menú desplegable o una lista de elementos, respectivamente, de los
cuáles sólo podemos seleccionar un elemento. La utilización de estas tres etiquetas varía muy
ligeramente, por ejemplo, h:selectOneListBox es la única que define un atributo size para
especificar el número de elementos que se muestran de la lista.
<h:selectOneMenu id="shippingOption"
required="true"
value="#{cashierBean.shippingOption}">
<f:selectItem itemValue="2"
itemLabel="#{bundle.QuickShip}"/>
<f:selectItem itemValue="5"
itemLabel="#{bundle.NormalShip}"/>
<f:selectItem itemValue="7"
itemLabel="#{bundle.SaverShip}"/>
</h:selectOneMenu>
El atributo value de selectOne... contendrá el valor seleccionado de entre todos los elementos
(también podríamos usar un atributo binding y asociar un objeto que representa al componente).
Cada valor posible a seleccionar lo especificamos con una etiqueta f:selectItem.
Muchas veces necesitamos especificar el conjunto de elementos posible de forma dinámica. Para
ello utilizamos, no etiquetas f:selectItem, sino una única etiqueta f:selectItems:
<h:selectOneMenu id="comboRegiones"
value="#{exCmBean.regionSeleccionada}"
onchange="submit()">
<f:selectItems value="#{exCmBean.regionesEnCombo}" />
</h:selectOneMenu>
El atributo value de la etiqueta f:selectItems hace referencia a una propiedad de un bean que
contiene los elementos a mostrar en la lista. Esta propiedad puede ser de tipo Array, Map o
Collection:
En caso de ser un Map, la clave de cada objeto indica el valor asociado al elemento
correspondiente de la lista, y el objeto se usa para imprimir el label asociado a dicho
elemento.
En cualquier otro caso, los elementos del array o colección pueden ser objetos SelectItem o
POJOs.
Un objeto SelectItem contiene propiedades value, label o disabled, que nos permiten
configurar cada elemento a dibujar en la lista.
En caso de utilizar POJOs, el framework, en su forma más simple, utiliza el mismo valor
para el value y label de cada elemento; o podemos tener un control más complejo del
resultado, de esta forma:
<h:selectOneMenu value="#{bean.countryCode}" required="true">
<f:selectItem itemValue="#{null}"
itemLabel="-- select one –"
noSelectionOption="true"/>
<f:selectItems value="#{bean.countries}" var="country"
itemValue="#{country.code}" itemLabel="#{country.name}" />
</h:selectOneMenu>
Aquí estamos utilizando dos etiquetas f:selectItem y f:selectItems diferentes para mostrar los
elementos. La etiqueta f:selectItem muestra un elemento no seleccionable (noSelectionOption =
"true") que provocará un error de validación en caso de que este elemento esté seleccionado al
enviar el formulario (para que este error de validación se produzca es necesario también que la
selección de un elemento sea obligatoria, como en este caso).
La etiqueta f:selectItems declara varios atributos. El atributo value enlaza con una propiedad array
o colección del bean bean. Esta colección contiene objetos Country con una serie de propiedades a
las que accedemos directamente gracias a la declaración del atributo var.
- h:selectManyCheckBox, h:selectManyMenu y h:selectManyListbox: Estas etiquetas funcionan
igual que las etiquetas h:selectOne... con la diferencia de que podemos seleccionar múltiples
valores, por lo que su atributo value debe apuntar a una List, un array de objetos o un array de tipos
primitivos.
<h:selectManyListbox id="comboProvincias"
value="#{exCmBean.provinciasSeleccionadas}" >
<f:selectItems value="#{exCmBean.provinciasEnCombo}" />
</h:selectManyListbox>
- h:dataTable: Esta etiqueta nos permite generar tablas dinámicas.
<h:dataTable id="empleados" rendered="#{gestorEmpleados.listarEmpleados}"
value="#{gestorEmpleados.empleados}"
var="emp">
<h:column>
<f:facet name="header">
<h:outputText value="CIF" />
</f:facet>
<h:outputText value="#{emp.cif}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="NOMBRE" />
</f:facet>
<h:outputText value="#{emp.nombre}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="APELLIDOS" />
</f:facet>
<h:outputText value="#{emp.apellidos}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="EDAD" />
</f:facet>
<h:outputText value="#{emp.edad}" />
</h:column>
</h:dataTable>
En este ejemplo mostramos una tabla en la que cada fila corresponde a un elemento de la colección
gestorEmpleados.empleados. El atributo value de un dataTable referencia a los datos a mostrar en la
tabla, que pueden tomar las siguientes formas:
Una lista de beans
Un array de beans
Un único bean
Un objeto javax.faces.model.DataModel
Un objeto java.sql.ResultSet
Un objeto javax.servlet.jsp.jstl.sql.Result
Un objeto javax.sql.RowSet
Todos estos tipos son encapsulados en un DataModel. Si no especificamos un DataModel, la
implementación JSF proporciona uno que encapsula los datos subyacentes.
La etiqueta h:dataTable permite también mostrar un subconjunto de los datos disponibles mediante
los atributos first y rows. El atributo first especifica el primer elemento a mostrar. El atributo
rows especifica el número de filas a mostrar. Por defecto ambos atributos tienen un valor de 0.
- h:message y h:messages: Estas etiquetas nos permiten mostrar mensajes de error asociados a
validaciones o conversiones de componentes. h:message muestra un mensaje asociado a un
componente (si hay varios, el primero que encuentre), mientras que h:messages muestra todos los
mensajes asociados a un componente, todos los mensajes de todos los componentes y/o mensajes
globales no asociados a ningún componente.
<p>
<h:inputText id="userNo"
title="Type a number from 0 to 10:"
value="#{userNumberBean.userNumber}">
<f:validateLongRange minimum="#{userNumberBean.minimum}"
maximum="#{userNumberBean.maximum}"/>
</h:inputText>
<h:commandButton id="submit" value="Submit"
action="response"/>
</p>
<h:message showSummary="true" showDetail="false"
style="color: #d20005;
font-family: 'New Century Schoolbook', serif;
font-style: oblique;
text-decoration: overline"
id="errors1"
for="userNo"/>
En este ejemplo se van a mostrar todos los errores de validación y conversión asociados al
inputText con id userNo debajo del botón submit. Podemos ver cómo estamos registrando un
validador estándar para limitar el rango posible de valores a insertar entre userNumberBean.minimum
y userNumberBean.maximum.
La etiqueta h:messages declara, entre otros, los atributos globalOnly (por defecto igual a false) y
for. Si el atributo for es especificado, se muestran todos los mensajes asociados al componente
indicado. Si for no es especificado y globalOnly toma el valor de true se muestran únicamente los
mensajes globales (no asociados a ningún id de cliente). Si for no es especificado y globalOnly es
false, se muestran todos los mensajes asociados a la página.
¿Cómo especificamos el mensaje a mostrar?
Por defecto JavaServer Faces proporciona unos mensajes por defecto asociados a unos códigos.
Para empezar a personalizar este mensaje lo primero sería añadir el atributo label al componente
que estamos validando, de esta forma:
<h:inputText id="userNo" title="Type a number from 3 to 6:"
label="User number">
<f:validateLongRange
minimum="3"
maximum="6"/>
</h:inputText>
Esto muestra el mensaje estándar de validación long range pero sustituyendo en el mensaje el id del
componente por la expresión indicada en la label.
Si queremos sustituir el mensaje completamente una opción sería asignar el nuevo mensaje al
código correspondiente en el fichero de recursos (ver capítulo referente a i18n). Los códigos
estándar de mensajes se encuentran encuentran en la especificación de jsf, capítulo 2.5.2.4, o en el
fichero Messages.properties del paquete javax.faces del fichero jsf-api...jar. Por ejemplo el código
javax.faces.component.UIInput.REQUIRED indica el mensaje a mostrar en caso de que un
componente sea obligatorio y no se suministre valor alguno.
Otra posibilidad es especificar el mensaje a mostrar como atributo del componente correspondiente,
por ejemplo:
<h:inputText id="textoOrig" value="#{exCmBean.textoOriginal}"
required="true" requiredMessage="Este campo no puede estar vacío"/>
En este caso el valor del atributo requiredMessage sustituiría al mensaje por defecto en caso de que
no se proporcionara ningún valor al inputText.
Podemos hacer lo mismo para el resto de validadores estándar con el atributo validatorMessage del
componente a validar:
<h:inputText id="userNo" title="Type a number from 3 to 6:"
validatorMessage="Number must be betwee 3 and 6!!!">
<f:validateLongRange
minimum="3"
maximum="6"/>
</h:inputText>
Eso sí, si quisiéramos especificar varios validadores para el mismo componente y utilizar mensajes
personalizados para cada validador, tendríamos que utilizar la técnica de sustitución de los mensajes
de error estándar en el fichero de recursos.
Finalmente, en caso de utilizar validadores personalizados podremos especificar de forma
programática el mensaje de error asociado a esa validación. Veremos esta técnica en el capítulo
dedicado a validadores.
Ejemplos de Uso de Etiquetas Html
En este capítulo vamos a ver un ejemplo de utilización de menús y radio buttons para ver más
claramente cómo utilizar las etiquetas <f:selectItem... y <f:selectItems.... Además utilizaremos un
hiperenlace con un action de navegación implícita, y un Managed Bean con un ámbito de sesión
para mantener el número de accesos a la página durante esa sesión. La ventana inicial es la
siguiente:
Cuando elijamos una región del menú de la izquierda, se rellenará el combo de la derecha con las
provincias de esa región:
Y al elegir uno de los radio buttons aparecerá un mensaje relacionado:
Tenemos también dos elementos command, uno es un botón y el segundo un hiperenlace. Ambos
navegan a la página principal, además el botón invoca un action que actualiza el número de visitas a
la página en esta sesión, y el hiperenlace invoca otro action que invalida la sesión actual.
Código de la aplicación
rellenacombos.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:f="http://java.sun.com/jsf/core">
<h:head>
<title>Rellenado de combos</title>
<meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8" />
</h:head>
<h:body>
<h1>Rellenado de combos</h1>
<p>Cuando elijas una región en el combo de la izquierda, se rellenará la lista de la
derecha con sus provincias correspondientes</p>
<h:form id="formcombos">
<p>
<h:panelGrid columns="3">
<h:selectOneMenu id="comboRegiones"
value="#{exCmBean.regionSeleccionada}"
onchange="submit()">
<f:selectItems value="#{exCmBean.regionesEnCombo}" />
</h:selectOneMenu>
<h:selectManyListbox id="comboProvincias"
value="#{exCmBean.provinciasSeleccionadas}" >
<f:selectItems value="#{exCmBean.provinciasEnCombo}" />
</h:selectManyListbox>
<h:selectOneRadio layout="pageDirection"
value="#{exCmBean.radioBtSelected}"
onchange="submit()"
valueChangeListener="#{exCmBean.nuevoRBSeleccionado}">
<f:selectItem itemValue="10" itemLabel="Mostrar mi color favorito"/>
<f:selectItem itemValue="15" itemLabel="Mostrar mi canción favorita"/>
<f:selectItem itemValue="22" itemLabel="Mostrar mi libro preferido"/>
</h:selectOneRadio>
</h:panelGrid>
</p>
<p>
Has accedido a esta página #{sesBean.accesosAPagina}
<h:outputText value="#{sesBean.accesosAPagina > 1 ? 'veces' : 'vez'}" />
</p>
<p>#{exCmBean.mensajeFavorito}</p>
<p>
<h:panelGrid columns="2">
<h:commandButton immediate="true"
value="Volver al índice"
action="#{sesBean.salir}" />
<h:commandLink immediate="true"
value="Cerrar la sesión"
action="#{sesBean.logout}" />
</h:panelGrid>
</p>
</h:form>
</h:body>
</html>
Podemos diferenciar cuatro partes dentro de la página. En la primera insertamos el título y damos
información al usuario; la segunda agrupa las listas de regiones y provincias y los radio button; la
tercera engloba los mensajes de número de visitas y elemento favorito, y la cuarta incluye al botón
de regreso a la página principal y al hiperenlace para invalidar la sesión.
<meta...: esta etiqueta Html es necesaria cuando enviamos valores al servidor con caracteres
especiales (en este caso acentos, como en Aragón). Con ella especificamos la codificación exacta
de la página de petición.
<h:selectOneMenu id="comboRegiones" value="#{exCmBean.regionSeleccionada}"
onchange="submit()">: combo de regiones declarado como un selectOneMenu, con lo que
tendremos un menú desplegable del que sólo podremos seleccionar un elemento al mismo tiempo.
El valor seleccionado se almacena en la propiedad regionSeleccionada del bean exCmBean y
declaramos el atributo onchange para que el formulario sea procesado cuando se seleccione un
nuevo valor del menú.
<f:selectItems value="#{exCmBean.regionesEnCombo}" />: etiqueta core para rellenar los valores
del menú de regiones de forma dinámica a partir de la propiedad regionesEnCombo del bean
exCmBean.
<h:selectManyListbox id="comboProvincias"...: declaramos una lista de selección múltiple
rellena de forma dinámica con las provincias correspondientes a la región seleccionada en el menú
anterior.
<h:selectOneRadio layout="pageDirection"...: conjunto de radio buttons dibujados en vertical
(layout="pageDirection") a los que damos valores de forma estática mediante tres etiquetas
<f:selectItem. Declaramos también un atributo valueChangeListener para que se invoque el
método nuevoRBSeleccionado del bean exCmBean cuando seleccionemos un nuevo radio button.
Más abajo accedemos al bean de sesión (sesBean) para especificar el número de veces que hemos
entrado en esta página (resaltar el uso del operador ? : dentro de la expresión JSF) e imprimimos el
mensaje correspondiente al radio button seleccionado. Por último declaramos un elemento
<h:commandButton y otro <h:commandLink cuyos action hacen referencia a métodos de exCmBean.
Managed Bean de petición:
@ManagedBean(name="exCmBean")
@RequestScoped
public class EjemplosCompBean {
/* Propiedades rellena combos */
private SortedMap<String, SortedSet<String>> mpRegionesProvincias =
new TreeMap<String, SortedSet<String>>();
private String regionSeleccionada = "Aragón";
private String[] provinciasSeleccionadas;
/* Propiedades radio buttons */
private int radioBtSelected;
private String mensajeFavorito;
/**
* Aprovechamos para inicializar las propiedades necesarias
*/
public EjemplosCompBean() {
SortedSet<String> sAux = new TreeSet<String>();
sAux.add("Cáceres");
sAux.add("Badajoz");
mpRegionesProvincias.put("Extremadura", new TreeSet<String>(sAux));
sAux.clear();
sAux.add("Lleida");
sAux.add("Barcelona");
sAux.add("Tarragona");
sAux.add("Girona");
mpRegionesProvincias.put("Catalonia", new TreeSet<String>(sAux));
sAux.clear();
sAux.add("Huesca");
sAux.add("Zaragoza");
sAux.add("Teruel");
mpRegionesProvincias.put("Aragón", new TreeSet<String>(sAux));
}
/* Métodos rellena combos */
public String getRegionSeleccionada() {
return regionSeleccionada;
}
public void setRegionSeleccionada(String regionSeleccionada) {
this.regionSeleccionada = regionSeleccionada;
}
public String[] getProvinciasSeleccionadas() {
return provinciasSeleccionadas;
}
public void setProvinciasSeleccionadas(String[] provinciasSeleccionadas) {
this.provinciasSeleccionadas = provinciasSeleccionadas;
}
public Collection<String> getRegionesEnCombo() {
return mpRegionesProvincias.keySet();
}
public Collection<String> getProvinciasEnCombo() {
return mpRegionesProvincias.get(this.regionSeleccionada);
}
/* Métodos radio buttons */
public int getRadioBtSelected() {
return radioBtSelected;
}
public void setRadioBtSelected(int radioBtSelected) {
this.radioBtSelected = radioBtSelected;
}
public void nuevoRBSeleccionado(ValueChangeEvent event) {
int actualValue = (Integer) event.getNewValue();
switch (actualValue) {
case 10: mensajeFavorito = "Mi color favorito es el ¡rojo!";
break;
case 15: mensajeFavorito = "Mi canción favorita es Nutshell";
break;
case 22: mensajeFavorito = "Mi libro preferido es... no sé... hay tantos...";
break;
}
}
public String getMensajeFavorito() {
return mensajeFavorito;
}
}
Declaramos el ManagedBean asignándole un alias y especificando que su ámbito va a ser de petición.
Los datos de regiones y sus provincias los vamos a almacenar dentro de un mapa en el que la clave
es el nombre de la región y el valor un conjunto ordenado con los nombres de sus provincias.
Aprovechamos el constructor del bean para inicializar este mapa.
El único método no trivial en este código es el oyente de cambio de valor (public void
nuevoRBSeleccionado(ValueChangeEvent event)). Como explicamos en el capítulo sobre el ciclo de
vida de una petición JSF los eventos de cambio de valor son procesados antes de que se hayan
actualizado las propiedades relacionadas en el bean correspondiente. Es por ello que en este caso no
podríamos hacer referencia a la propiedad regionSeleccionada, pues el valor devuelto sería el valor
antiguo, y no el seleccionado actualmente. Sin embargo, el objeto Event recibido sí que contiene ese
valor actualizado, y es de donde lo obtenemos para poder actualizar el mensaje correspondiente.
Managed Bean de sesión:
@ManagedBean(name="sesBean")
@SessionScoped
public class SessionManagedBean {
private int accesosAPagina = 1;
public int getAccesosAPagina() {
return accesosAPagina;
}
public String salir() {
accesosAPagina++;
return "index";
}
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/index.xhtml?faces-redirect=true";
}
}
Este bean lo utilizamos para mantener el número de accesos a esta página en esta sesión y procesar
los eventos action asociados al botón y al hiperenlace. Hemos optado por utilizar un bean diferente
para mostrar la posibilidad de utilizar diferentes Managed Beans en una misma página; podríamos
haber optado por gestionar todos los componentes de la página en un único bean, o incluso utilizar
un mismo bean para diferentes páginas.
Este bean es muy sencillo; resaltamos el uso de la anotación @SessionScoped para mantener la
misma instancia del bean durante llamadas y mantener de esta manera el valor de la propiedad
accesosAPagina; el acceso a 'métodos globales' de una aplicación JSF mediante la clase
FacesContext, en este caso para poder invalidar la sesión; y la utilización del parámetro faces-
redirect=true cuando queremos que el resultado de un action sea una redirección, y no un reenvío.
Hablaremos más detalladamente sobre los posibles ámbitos de un bean en el capítulo dedicado a los
Managed Beans.
Ciclo de vida de una petición JSF
JSF establece un ciclo de vida de múltiples fases para cada petición JSF que se envía al servidor.
Estas fases incluyen todas las tareas que se realizan en el servidor desde que la petición es recibida
hasta que se envía la respuestas. Cada una de las fases tiene una funcionalidad bien definida, y
aunque no es necesario conocerlas para implementar aplicaciones JSF, un conocimiento mínimo de
las distintas fases puede resultar de gran ayuda al desarrollador de aplicaciones.
Antes de explorar cada una de las fases, hablemos de los elementos involucrados y las tareas que es
necesario realizar en el servidor durante el procesamiento de la petición y la respuesta.
La información que envía el cliente al servidor en una petición http está siempre codificada en
formato de String (a menos que se trate de un fichero adjunto). Es necesario convertir estas
cadenas de texto a los tipos reales con los que trabaja nuestra aplicación. Para ello JSF define una
serie de conversores estándar que pueden ser ampliados en caso de que determinados valores
necesiten una conversión más personalizada.
JSF trabaja en el lado servidor con un árbol de componentes que representa la página facelet
(vista) que ha enviado el cliente. Este árbol de componentes se crea en la primera petición sobre
esa página, y se reutiliza en subsiguientes peticiones. La forma de saber si estamos en la primera
petición es por el todo http de petición. Cuando es una petición get JSF asume que se trata de
la primera petición del ciclo para esa página, y crea el árbol de componentes asociado (un objeto
componente en el lado servidor por cada componente declarado mediante etiquetas en el facelet).
Si se trata de una petición post JSF recupera el árbol de componentes que creó previamente para
dicha página.
Si pasamos a otra página, el ciclo comienza de nuevo y el árbol actual se destruye.
Una vez que se ha copiado la información que envía el cliente a los componentes del árbol, los
datos se validan y se convierten. Si no se produce ningún error estos datos ya convertidos se
almacenan en las propiedades de los ManagedBean correspondientes. Si existe algún error de
conversión o validación el framework vuelve a dibujar la página facelet original con los valores
contenidos en el árbol de componentes, para que puedan ser mostrados al cliente y éste corrija los
datos erróneos.
Como ya hemos comentado, JSF actúa de forma diferente ante una petición get y una petición post.
En el primer caso el árbol de componentes es creado de cero a partir de los componentes definidos
en la página facelet. Además en este caso únicamente se ejecutan las fases Restore View y Render
Response, mientras que ante una petición post el framework recupera el árbol de componentes
previamente creado e intenta ejecutar todas las fases del ciclo de vida.
Restore view phase
La fase de Reconstrucción de la Vista es la fase inicial en el procesamiento de una petición JSF.
Durante esta fase, la implementación JavaServer Faces construye el árbol de componentes de la
página facelet, lo almacena en el FacesContext, y conecta los manejadores de eventos, validadores
y conversores a los distintos componentes del árbol.
Si se trata de una petición inicial (get), el framework JSF crea una vista vacía y avanza
directamente a la fase Render Response (Generación de la Respuesta), donde la vista se rellenará
con los componentes correspondientes.
Si se trata de un postback la vista ya existe en el FacesContext. En este caso, simplemente se
recupera y se actualiza con la información almacenada en el cliente o el servidor.
Apply Request Values Phase
En esta fase cada parámetro que envía el cliente se copia al componente correspondiente del árbol,
utilizando el método decode de cada componente, y se realizan las conversiones. Si hay
componentes de la página con un valor de true para su atributo immediate, la validación y
procesamiento de eventos de dichos componentes se realizan en esta fase. Si se produce algún error
de conversión o validación se genera un mensaje de error asociado con el componente
correspondiente y se encola en el FacesContext. Este mensaje se mostrará durante la fase
Renderizar la Respuesta.
Al final de esta fase, los componentes del árbol tienen sus valores actualizados, y puede haber
nuevos mensajes y eventos encolados.
Process Validations Phase
Durante esta fase se procesan todas las validaciones registradas en el árbol de componentes. Si un
valor incumple una regla, la implementación JavaServer Faces añade un mensaje de error al
FacesContext y el ciclo de vida avanza directamente hasta la fase Rendering Response para que la
página sea dibujada de nuevo incluyendo los mensajes de error. Esta alteración del flujo de
ejecución se produce también si hubo algún error de conversión en la fase Apply Request Values.
Si la validación de un componente es correcta, su atributo immediate es false y tiene asociado un
valueChangeListener, este valueChangeListener se invoca en este momento y a continuación se
realiza la validación del siguiente componente.
Es importante resaltar este hecho: los eventos valueChange, cuando immediate es igual a false, son
procesados en esta fase, cuando aún no se han actualizado los valores del modelo, hecho que se
produce en la fase siguiente. Por ello, si necesitamos acceder al valor actual del componente lo
haremos a través del objeto Event que recibimos como parámetro del método oyente.
Existe otro mecanismo para resolver este problema que consiste en encolar de nuevo el evento y
configurarlo para que se procese durante la fase de invocación de la aplicación. Más abajo
ofrecemos una discusión detallada al respecto.
Update Model Values Phase
Una vez que la implementación JavaServer Faces determina que los datos son válidos, recorre el
árbol de componentes y actualiza los valores del modelo con los valores pasados en la petición. La
implementación de JSF actualiza únicamente las propiedades de un bean referenciadas por atributos
value. Si el dato local no se puede convertir a los tipos especificados por los atributos del bean, el
ciclo de vida avanza directamente a la fase Render Response para redibujar la página mostrando los
errores de forma similar a lo que sucede cuando hay errores de validación.
Invoke Application Phase
Durante esta fase, la implementación JavaServer Faces maneja cualquier evento a nivel de
aplicación, como enviar un formulario o enlazar a otra página. A continuación transfiere el control a
la fase Render Response.
Render Response Phase
En esta fase el framework construye la vista y delega la responsabilidad de formatear la respuesta
en la tecnología correspondiente (normalmente páginas facelets).
Si se trata de una petición inicial se añaden los componentes representados en la página al árbol de
componentes.
Si la petición es un postback y se produjo algún error en las fases Apply Request Values, Process
Validations, o Update Model Values, se dibuja de nuevo la página original. Si ésta contiene alguna
etiqueta h:message o h:messages, se muestran los mensajes de error de la cola de mensajes.
Tras renderizar el contenido de la vista se graba el estado de la respuesta para poder acceder a ella
posteriormente.
El atributo immediate
El atributo immediate determina la fase en la que se procesan los eventos asociados a un
determinado componente. Como sabemos, hay dos tipos de eventos de aplicación: eventos action y
eventos valueChange. Por la arquitectura intrínseca Html, cuando modificamos el valor de un
componente en la página ésta no es enviada al servidor para su procesamiento a menos que lo
indiquemos explícitamente en su código mediante javascript (mediante el atributo
onclick=”submit()”), cosa que no sucede con los eventos action. Es decir, cuando pulsamos un
botón o un hiperenlace únicamente se lanza el evento action asociado a ese botón o hiperenlace,
pero pueden lanzarse al mismo tiempo eventos valueChange de componentes que hayamos
modificado sin enviar el formulario. De la misma manera, si al modificar el valor de un componente
enviamos el formulario explícitamente, no va a haber ningún evento action a procesar, pero puede
haber otros eventos valueChange de componentes modificados previamente.
El procesamiento del ciclo de fases de la página Jsf depende entonces de las siguientes reglas:
para cualquier componente que lance eventos action, si el valor de su atributo immediate es
false, el procesamiento de dicho evento se realizará durante la fase Invoke Application
(primero se ejecutará el método asociado a su atributo actionListener y a continuación el
asociado a su atributo action).
para cualquier componente que lance eventos action, si el valor de su atributo immediate es
true, el procesamiento de dicho evento se realizará durante la fase Apply Requests Values,
y a continuación pasaremos directamente a la fase de Render Response, saltándonos el resto
de fases y por consiguiente el procesamiento de eventos valueChange que no tengan un valor
de su atributo immediate=”true”.
para cualquier componente que lance eventos valueChange, si el valor de su atributo
immediate es false, el procesamiento de dicho evento se realizará durante la fase Process
Validations. Un componente de este tipo no tiene su propio 'ciclo de fases', es decir, en esta
fase se valida el componente, si esta validación es correcta se ejecuta su listener asociado y a
continuación pasamos al siguiente componente a validar. Cuando todos los componentes
apropiados han sido validados y se han procesado los eventos correspondientes, si no se ha
producido ningún error de validación continuamos el flujo de procesamiento y pasamos a la
fase Update Model Values; si se han producido errores de validación se encolan los mensajes
correspondientes y pasaríamos directamente a la fase Render Response.
para cualquier componente que lance eventos valueChange, si el valor de su atributo
immediate es true, la validación del componente y el procesamiento de dicho evento se
realizarán durante la fase Apply Requests Values. Esto se realiza para todos los
componentes cuyo valor haya sido modificado y tengan un valor de immediate igual a true.
Luego se continua el procesamiento de la petición según las reglas ya explicadas, es decir, si
ha habido algún error de conversión/validación, pasamos directamente a la fase Render
Response, y si no lo ha habido, dependemos de en qué circunstancias se haya enviado el
formulario:
Si el formulario se ha enviado de forma explícita al modificar un valor, pasamos a la fase
de Process Validations, donde se procesarán las validaciones de componentes con un
valor de atributo immediate igual a false.
Si se ha enviado por la pulsación de un botón o un hiperenlace y el atributo immediate de
este componente es true, se ejecuta su action correspondiente y pasamos directamente a
la fase de Render Response, por lo que NO se validan el resto de componentes de la
página ni se procesan otros eventos que hayan sido lanzados.
Si se ha enviado por la pulsación de un botón o un hiperenlace y el atributo immediate de
este componente es false, pasamos a la fase de Process Validations y se continúa el
ciclo de fases sin ninguna alteración.
Ejemplos reales de uso de immediate=”true”
Si se usa únicamente en componentes UIInput, la validación de estos componentes se
realizará durante la fase de Apply Request Values. Esto es útil cuando queramos priorizar la
validación de determinados componentes. Cuando la conversión o validación de uno de
estos componentes falle, los componentes cuyo valor de immediate sea false no serán
convertidos ni validados, por lo que sólo se mostrarán los mensajes de error de los
componentes priorizados.
Si se usa únicamente en componentes UICommand, pasaremos directamente de la fase Apply
Request Values a la fase Render Response. Útil cuando queremos evitar totalmente el
procesamiento del formulario (por ejemplo en botones Cancel o Back).
Si se usa tanto en componentes UIInput como en UICommand, pasaremos directamente de la
fase Apply Request Values a la fase Render Response, pero se realizará la conversión,
validación y procesamiento de eventos de los componentes UIInput modificados cuyo valor
de immediate sea true. Usaremos esto para omitir el procesamiento de todo el formulario
excepto para determinados campos. Por ejemplo, un botón de Olvidé mi contraseña en un
formulario con un campo de Contraseña required pero no immediate.
Pushback de eventos valueChange
Un valueChangeListener es invocado durante la fase de Apply Request Values, si el atributo
immediate del componente que lanzó el evento tiene un valor de true, o durante la fase Process
Validations si el valor de immediate es false. En ambos casos el método oyente es invocado antes
de la fase Update Model Values, es decir, antes de que se actualice la propiedad asociada al
componente con el nuevo valor de éste. Por ello, si necesitamos acceder a ese valor nuevo debemos
recuperarlo a partir del evento que se nos pasa como parámetro, como ya hemos visto en algún
ejemplo:
public void nuevoRBSeleccionado(ValueChangeEvent event) {
int actualValue = (Integer) event.getNewValue();
switch (actualValue) {
...
}
Otra solución es retrasar (pushback) el procesamiento del evento a una fase más avanzada en la que
ya se haya actualizado el modelo:
public void nuevoRBSeleccionado(ValueChangeEvent event) {
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return;
}
int actualValue = radioBtSelected;
...
}
En este código le preguntamos al evento por la fase en la que está siendo procesado, y si es
diferente de Invoke Application le cambiamos el identificador de fase y lo encolamos de nuevo. De
esta manera nos aseguramos de que se procese durante la fase indicada.
¿Cuándo usar un método u otro? Bueno, eso depende de nuestra intención. La primera solución nos
asegura que el código del oyente se ejecute existan o no errores de validación en otros componentes
de la página. La segunda solución nos asegura que el código del oyente se ejecuta sólo si no existen
errores de validación en ningún componente y cuando ya se han actualizado los valores del modelo.
Objetos implícitos en una página jsf
- applicationScope: A Map of the application scope attribute values, keyed by attribute name
- cookie: A Map of the cookie values for the current request, keyed by cookie name
#{cookie.userCookie.value} or
#{cookie["userCookie"].value}
- facesContext: The FacesContext instance for the current request
#{facesContext.externalContext.session.id}
- header: A Map of HTTP header values for the current request, keyed by header name
#{header.Accept} or #{header["Accept"]}
#{header["Accept-Encoding"]}
- headerValues: A Map of String arrays containing all the header values for HTTP headers in the
current request, keyed by header name
- initParam: A Map of the context initialization parameters for this web application
- param: A Map of the request parameters for this request, keyed by parameter name
#{param.custID}
- paramValues: A Map of String arrays containing all the parameter values for request parameters in
the current request, keyed by parameter name
- requestScope: A Map of the request attributes for this request, keyed by attribute name
- sessionScope: A Map of the session attributes for this request, keyed by attribute name
- view: The root UIComponent in the current component tree stored in the FacesRequest for this
request
Validación
Validadores estándar:
Validator Class Tag Function
BeanValidator validateBean Registers a bean validator for the component.
DoubleRangeValidator validateDoubleRange Checks whether the local value of a component is within a certain range.
The value must be floating-point or convertible to floating-point.
LengthValidator validateLength Checks whether the length of a component’s local value is within a
certain range. The value must be a java.lang.String.
LongRangeValidator validateLongRange Checks whether the local value of a component is within a certain range.
The value must be any numeric type or String that can be converted
to a long.
RegexValidator validateRegEx Checks whether the local value of a component is a match against a
regular expression from the java.util.regex package.
RequiredValidator validateRequired Ensures that the local value is not empty on
an javax.faces.component.EditableValueHolder compo
nent.
Si necesitamos validación personalizada la podemos implementar a través de métodos de beans o
mediante un validador propio.
1. A través de métodos de beans:
public void validatePlayer3(FacesContext context, UIComponent component, Object
value) throws ValidatorException {
String selectedRadio = (String) value;
String msg =
"V3-- Since you indicated that you play Tennis, Please enter Club Name.";
String clubNameFromComp = (String) clubNameBind.getSubmittedValue();
if ((clubNameFromComp == null || "".equalsIgnoreCase(clubNameFromComp)) &&
"Y".equalsIgnoreCase(selectedRadio)) {
throw new ValidatorException(
new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg));
}
}
<h:selectOneRadio id="sportsPer" value="#{personView.per.likesTennis}"
validator="#{personView.validatePlayer3}" title="Play tennis" tabindex="6">
2. Mediante un validador propio:
@FacesValidator("emailValidator")
public class EmailValidator implements Validator
// Declaración e inicialización del pattern
@Override
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
matcher = pattern.matcher(value.toString());
if (!matcher.matches()) {
FacesMessage msg =
new FacesMessage("E-mail validation failed.",
"Please provide E-mail address in this format: abcd@abc.com");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
}
<h:inputText id="email" value="#{personView.per.email}"
required="true" requiredMessage="email is required"
title="Primary Email" tabindex="3">
<f:validator validatorId="emailValidator" />
</h:inputText>
Conversores
Tenemos los conversores estándar:
<h:outputText value="#{cashierBean.shipDate}">
<f:convertDateTime pattern="EEEEEEEE, MMM dd, yyyy" />
</h:outputText>
...
<h:outputText value="#{cashierBean.shipDate}">
<f:convertDateTime dateStyle="full"
locale="es"
timeStyle="long" type="both" />
</h:outputText>
Y conversores personalizados:
@FacesConverter("pacienteConverter")
public class PacienteConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component,
String newValue) throws ConverterException {
try {
ConsultasDAO dao = (ConsultasDAO)
context.getApplication().getVariableResolver().
resolveVariable(context, "daoIbatis");
Paciente paciente = dao.getPacienteXId(Integer.parseInt(newValue));
return paciente;
} catch (Exception e) {
((ServletContext) context.getExternalContext().getContext()).
log("Error en el getAsObject de PacienteConverter", e);
throw new ConverterException(e);
}
}
public String getAsString(FacesContext context, UIComponent component,
Object value) throws ConverterException {
if (value==null) return "";
try {
Paciente paciente = (Paciente) value;
return Integer.toString(paciente.getId());
} catch (Exception e) {
((ServletContext) context.getExternalContext().getContext()).
log("Error en el getAsString de PacienteConverter", e);
throw new ConverterException(e);
}
}
}
<h:inputText value="#{pacienteForm.paciente}" converter="pacienteConverter"/>
Ajax y jsf
Ejemplo:
<h:selectOneRadio id="edadRadio" layout="lineDirection"
value="#{gestorEmpleadosLista.edadAMostrar}">
<f:selectItem itemValue="0"
itemLabel="&lt; 18"/>
<f:selectItem itemValue="1"
itemLabel="18 - 25"/>
<f:selectItem itemValue="2"
itemLabel="&gt; 25"/>
<f:selectItem itemValue="3"
itemLabel="todos"/>
<f:ajax execute="@this" render="listaEmpleados" />
</h:selectOneRadio>
En este ejemplo el execute=”@this” es redundante ya que es su valor por defecto. listaEmpleados
es el id de un <h:selectOneListbox que no se muestra en el ejemplo. Notar que hemos eliminado el
atributo onclick=”submit” del elemento <h:selectOneRadio.
La etiqueta <f:ajax puede tener los siguientes atributos:
event: el evento que dispara la petición ajax.
execute: los componentes que queremos que se procesen, separados por espacio. También
podemos utilizar @this, @form, @all y @none.
render: los componentes que queremos que se redibujen, separados por espacio.
Comunicación en Jsf 2
Ver http://balusc.omnifaces.org/2011/09/communication-in-jsf-20.html
Oyentes de fases
Implementar PhaseListener y registrarlo en el faces-config.xml:
</application>
<lifecycle>
<phase-listener>profe.jsfbasic.beans.ResumeBean</phase-listener>
</lifecycle>
I18N
Una opción es utilizar el faces-config.xml
<application>
<resource-bundle>
<base-name>resources.mensajes</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>
resources.ApplicationMessages
</message-bundle>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>es</supported-locale>
<supported-locale>de</supported-locale>
<supported-locale>fr</supported-locale>
</locale-config>
</application>
otra, cargar el fichero de recursos a nivel individual en la página
<f:loadBundle basename="messageresources" var="msg"/>
La diferencia entre resource-bundle y message-bundle es que en el segundo
únicamente puedo sustituir mensajes de error por defecto, el primero
es el que referencio directamente desde las jsf o desde código:
<h:outputText value="#{msg.presentacion}"/>
Los mensajes de error estándar se encuentran en la especificación de jsf,
capítulo 2.5.2.4, por ejemplo
javax.faces.component.UIInput.REQUIRED
* Acceder a los recursos idiomáticos desde código para mensajes de error en la página asociados a
un id:
FacesContext context = FacesContext.getCurrentInstance();
ResourceBundle bundle =
ResourceBundle.getBundle("resources.mensajes",
context().getViewRoot().getLocale());
String message = bundle.getString(key);
context().addMessage("logonForm:mailboxHost",
new FacesMessage(FacesMessage.SEVERITY_ERROR, message, message));
...
<h:form id="logonForm">
<h:message for="mailboxHost" errorClass="errorText"/>
</h:form>
Si no asociamos un mensaje a ningún componente, se considera un mensaje global (se muestra con
la etiqueta h:messages, siempre que el atributo for no esté definido):
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(message));
In addition to Daniel's answer you could always use the label attribute for your input components to
remove the client-id (j_idt10:userNo:) from the error message.
E.g. with
<h:inputText id="userNo" title="Type a number from 0 to 10:"
label="User number">
<f:validateLongRange
minimum="3"
maximum="6"/>
</h:inputText>
will produce:
User number: Validation Error: Specified attribute is not between the expected values
of 3 and 6.
The label attribute can be an el expression as well to change this part of the error message
dynamically.
* Cambiar el locale para la aplicación entera:
El locale en jsf se recupera por defecto por request, no por sesión como en struts. Por ello hay que
realizar dos pasos. El primero, cambiar el locale en la UIViewRoot y almacenarlo en un bean de
sesión:
FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale("ua"));
// almacenarlo en un bean de sesión
el segundo, en todas las páginas añadir el atributo locale a la vista:
<f:view locale="#{localeBean.locale}">
localeBean sería el bean de sesión en el que hemos almacenado el locale en el paso 1
Misc
* Saber qué botón hemos pulsado en un validador o conversor:
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
if (params.containsKey("formId:button1")) {
// Button 1 is pressed.
}
else if (params.containsKey("formId:button2")) {
// Button 2 is pressed.
}
* Almacenar atributos en la sesión:
FacesContext.getCurrentInstance().
getExternalContext().getSessionMap().
put("name", event.getNewValue());
* Diferencia entre binding y value: el primero enlaza con una propiedad que representa el
componente (HtmlInputText, HtmlOutputLabel...) mientras el segundo enlaza con una propiedad
simple que almacena el valor.
* Acceder a beans desde código:
javax.faces.context.FacesContext fc =
javax.faces.context.FacesContext.getCurrentInstance();
Object v = fc.getApplication().getVariableResolver().
resolveVariable(fc, "facesBean");
pageContext.setAttribute("beanName", v);
//To reset session bean
FacesContext
.getCurrentInstance()
.getApplication()
.createValueBinding( "#{yourBeanName}").
setValue(FacesContext.getCurrentInstance(), null );
//To get session bean reference
Object obj = FacesContext
.getCurrentInstance()
.getApplication()
.createValueBinding("#{yourBeanName}")
.getValue(FacesContext.getCurrentInstance());
YourSessionBean bean = (YourSessionBean)obj;
* Para pasar a otra fase de forma explícita
FacesContext.getCurrentInstance().renderResponse();
* El papel del atributo rendered: parece que cuando jugamos con el atributo rendered, éste va a
influir en que se procesen las validaciones o no del componente asociado. Si rendered=false en la
página resultante y el componente que hace submit del formulario tiene immediate=true, entonces el
componente no se valida
* Para tener diferentes archivos de configuración de faces:
A resource named /META-INF/faces-config.xml in any of the JAR files in the web application's
/WEB-INF/lib/ directory and in parent class loaders. If a resource with this name exists, it is
loaded as a configuration resource. This method is practical for a packaged library containing some
components and renderers.
A context initialization parameter, javax.faces.application.CONFIG_FILES, that specifies one or
more (comma-delimited) paths to multiple configuration files for your web application. This
method will most likely be used for enterprise-scale applications that delegate to separate groups the
responsibility for maintaining the file for each portion of a big application.
A resource named faces-config.xml in the /WEB-INF/ directory of your application. This is the way
most simple applications will make their configuration files available.
Managed Beans
Aplicación de Gestión
Vamos a desarrollar una aplicación de gestión básica para poner en práctica algunos de los
conocimientos que hemos adquirido. La aplicación va a consistir en una página de mantenimiento
de empleados que va a tener este diseño:
Cuando seleccionemos alguno de los empleados de la lista, sus datos completos aparecerán en los
campos de texto. Cuando pulsemos el botón de Listar, aparecerá una lista de todos los empleados
con todos sus datos y el botón cambiará su valor a Ocultar. Cuando volvamos a pulsarlo tomará de
nuevo el valor Listar y desaparecerá la lista de empleados. Los botones de Insertar, Modificar o
Eliminar realizan la función correspondiente, con la particularidad de que Modificar y Eliminar no
determinan el empleado a modificar o eliminar según el empleado seleccionado, sino que consultan
para ello el valor del campo de texto Cif. Además tenemos validaciones condicionales en función
del evento que se ha lanzado: si se ha seleccionado un empleado nuevo a través de la lista no
tenemos que realizar ninguna validación, al igual que si se pulsa el botón de Listar. Si pulsamos el
botón de Eliminar sólo es obligatorio el campo Cif, mientras que al pulsar Insertar o Modificar
todos los campos son obligatorios.
También haremos uso de las facilidades de i18n de JavaServer Faces para proporcionar acceso a
nuestra aplicación en otros idiomas:
Para esta aplicación vamos a hacer uso del estándar cdi de JEE. Como utilizamos Tomcat como
contenedor, necesitamos utilizar una implementación externa de dicho estándar. Vamos a utilizar
para ello Weld, de JBoss.
Necesitamos añadir entonces las siguientes librerías a nuestro proyecto:
- cdi-api-1.2.jar
- javax.inject-1.jar
- javax.el-api-3.0.0.jar
- javax.interceptor-api-1.2.jar
- weld-servlet-2.2.13.Final.jar
Las dos primeras deben estar en la ruta de clases de nuestro entorno para poder compilar contra
ellas.

Navigation menu