Está en la página 1de 126

JAVA EE7 – JSF

INDICE
1. INTRODUCCION: JAVA EE7 – JSF

2. Navegación

3. Ciclo de vida de una aplicación

4. Facelets (Parte I)

5. Facelets - Componentes compuestos (Parte II)

6. Recursos

7. Resource Library Contracts

8. HTML5

9. Lenguaje de expresiones

10. Usando Converters, Listeners, y Validators


1. INTRODUCCION: JAVA EE7 – JSF
¿Qué es JavaServer Faces?
Esta tecnología es un marco de trabajo del lado del servidor que permite
construir aplicaciones Java basadas en Web. Digo que es del lado del servidor,
porque se instala en el servidor y este genera las páginas web. Por otro lado,
un marco de trabajo del lado del cliente son los que se crean en el mismo
cliente web, como por ejemplo JQuery y Dojo.

Este marco de trabajo tiene bastantes características que permiten crear


aplicaciones web muy sofisticadas, de manera fácil y segura. (Parece un cliché
que mencionan en cualquier producto)

Partes de una aplicación JavaServer Faces


Una aplicación típica de JavaServer Faces comprende lo siguiente:

• Un conjunto de páginas web en la que se distribuyen los componentes.


• Un conjunto de etiquetas (tags) para agregar componentes a las
páginas.
• Un conjunto de beans administrados (ManagedBeans), que son
administrados por el contenedor usando objetos. Estos beans
administrados son los que están detrás de una página proporcionando
propiedades y funciones a la interfaz de usuario.
• Un archivo descriptor web (web.xml)
• Adicionalmente, archivos de configuración de recursos (faces-
config.xml)
• Además, objetos personalizados: validadores, convertidores, oidores
(listeners) para mejorar la aplicación.
• Y etiquetas adicionales personalizadas.

En la siguiente imagen se puede ver la interacción entre el cliente y el servidor


en una página JSF.
Primera aplicación JavaServer Faces
Sin mucho más preámbulo (y si te aburre la teoría, seguramente has venido
directamente a esta sección) crearemos nuestra primera aplicación en
NetBeans.

Para esto necesitaremos

• Java SE 7
• NetBeans 7.3 (con actualizaciones) o 7.3.1
• GlassFish 4.0 (y configurado con NetBeans)

1. Creando la aplicación

Como siempre, comenzamos por crear un nuevo proyecto desde File > New
Project (o Shift+Ctrl+N) y se nos presentará la ventana para crear un nuevo
proyecto. Seleccionamos la categoría web y en proyecto: "Web Application".
Clic en "Next".

Escribiremos el nombre jsf-01-primera-aplicacion.


Clic en "Next".
Seleccionamos el servidor GlassFish 4.0 y seleccionamos Java EE 7 Web.
Clic en "Next"

En las opciones de "Framework", activamos la opción "JavaServer Faces" y nos


aseguramos de que utilice en "Server Library" la opción JSF 2.2. Si no aparece
esta opción, es muy probable que no esté seleccionada la versión Java EE 7.
Clic en "Finish"
Listo, ya tenemos el proyecto JSF con una página.
Lo ejecutamos, y lo que nos muestra es algo muy sencillo.
2. Creando recurso de texto

Lo primero que recomiendo es no escribir texto alguno en la página. Es mejor


usar un recurso donde estén todos los mensajes a mostrar. Si necesitamos
hacer un cambio, en vez de buscar entre todas las páginas, solo cambiamos el
contenido del archivo de recursos. Estos archivos de recursos son
básicamente archivos .properties.

Comenzaremos con crear este archivo en File > New File (Ctrl + N) y
seleccionamos en categoría "Others" y el tipo de archivo "Properties file"

Clic en "Next"

Establecemos un nombre de archivo, en este caso se llamará "mensajes" y la


ruta donde se ubicará. Se deberá indicar como ruta de carpeta en formato de
paquete dentro de la carpeta "src", es decir, en lugar de colocar puntos (.)
como separador de paquete, colocaremos la barra inclinada (/).
(Posteriormente para acceder a ese archivo, lo haremos como una clase Java)

Clic en "Finish"

Notar como se ha creado la estructura de carpetas en el proyecto, y la


ubicación del archivo.
Ahora, escribamos un texto que usaremos en la aplicación. Será el título de la
aplicación. Luego, pondremos otro texto más, pero este se separará por
puntos.

1app_title=Mi primera aplicación JSF


2app.formulario=Directorio de contacto

Necesitamos usar este archivo de recursos en nuestra página. Por tanto,


regresemos a index.html y escribamos lo siguiente después del tag <head>
(Recomiendo mucho comenzar a escribir presionando Ctrl+Espacio. Esto
activará el autocompletado de código y ayudará enormemente la creación de
código) :

1<f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>

Notemos cómo se debe escribir para acceder al recurso. Como decía líneas
arriba, se va a acceder como una clase: organizada por paquetes y sin la
extensión ".properties". Ahora, para hacer referencia a ese recurso de
mensajes lo guardaremos en la variable "msg".

Sigamos con escribir dentro de <head> </head> escribir:

1<h:outputText value="#{msg.app_title}" />

Vemos que se coloca el nombre de la variable, seguido de la clave del texto


del properties separados por un punto.

Podemos ir ejecutando la aplicación, y veremos que aparecerá en el título del


navegador, el texto que hemos colocado.
Y ahora, agreguemos cómo título, el otro texto. Pero notemos que la clave de
este texto tiene puntos "app.formulario"

Para usarlo, escribiremos la clave encerrándolo entre corchetes:

1#{msg['app.formulario']}

Y al ejecutar la aplicación, nos mostrará:


Con esto ya comenzamos a tener un código limpio de textos, y únicamente
tendrán puras etiquetas.

Hemos comenzado con la capa de Vista. Ahora vamos a darle la vida a esta
vista usando la capa Controladora.

3. Controlador de JSF

El controlador de JSF está basado en un Bean. Este es el ManagedBean. Antes


se usaba la notación @ManagedBean, pero a partir de JavaEE7 se recomienda
usar un @Name. Ya que técnicamente hacen lo mismo, esto es para reducir
notaciones redundantes.

Se recomienda usar un Bean por cada vista, ya que serán las controladores de
las páginas.

Crearemos un nuevo archivo (Ctrl+N) y seleccionamos en la categoría


"JavaServer Faces" el tipo "JSF ManagedBean"
Clic en "Next"
Ahora, colocaremos el nombre de nuestra clase, el paquete, y otras
propiedades que tendrá el ManagedBean.

• Class Name: ContactoFormBean


• Package: com.apuntesdejava.jsf.controladores
• Name: contactoFormBean (se ha autogenerado)
• Scope: request
Clic en "Finish"

Si vemos la clase generada, es una clase común y corriente, solo con dos
notaciones adicionales @Named y @RequestScoped. La simplicidad de la creación
de clases desde JavaEE 6, nos permite que tengamos los componentes
únicamente con solo declarar la clase, sin ningún archivo adicional. Si
hubiéramos creado una clase simple, y le colocábamos esas notaciones,
tendríamos el mismo resultado.
Este ManagedBean será nuestra clase que permitirá controlar el formulario
para registrar los nombres de nuestro directorio de contactos.

Vamos a usar esta clase para que haga cosas simples, y a medida que vayamos
avanzando, vamos a ponerlo más interesante. Hagamos que reciba un
nombre, y luego que devuelva un saludo (Sí, el clásico "hola mundo")

Crearemos una propiedad en esta clase. Recordemos que es una clase POJO,
por tanto, debemos sujetarnos a las reglas de un JavaBean con respecto a las
propiedades. Crearemos una propiedad llamada "nombre" de tipo "String"
con sus respectivos accesorios.

Tenemos varias maneras: una es escribiendo todas las declaraciones; otra,


escribir la propiedad y usar la herramienta de encapsulamiento; o, haciendo
clic derecho en el código fuente y seleccionar "insert code" (o presionando
Alt+Insert)

Y escribimos el campo nombre, la palabra "nombre", y dejamos las demás


opciones en los valores por omisión.
Clic en "Ok". Y el código se habrá generado, con todo y su Javadoc. Si no
queremos que aparezca el Javadoc, desactivamos la creación en la ventana
anterior.
Además, agregaremos un método nuevo que permitirá devolver un saludo:

1public String getSaludo(){


2 return "Hola " +nombre;
3}

Pero como aún tiene hardcode, vamos a usar el mismo recurso para mostrar
el saludo. Para hacerla más simple, crearemos la clase ResourcesUtil y tendrá un
método:
1
2 package com.apuntesdejava.jsf.util;
3
import javax.faces.context.FacesContext;
4
5
6 public class ResourcesUtil {
7
8 public static String getString(String key) {
9 FacesContext context = FacesContext.getCurrentInstance();
10 String value = context.getApplication().evaluateExpressionGet(context, key, String.class);
11
return value;
12 }
13}
14

y ahora sí, nuestro método de saludo lucirá así:

1public String getSaludo(){


2 return ResourcesUtil.getString("#{msg['app.saludo']}") +nombre;
3}
Terminemos nuestro formulario en index.xhtml: Agregemos los controles
necesarios:

1
<h:form>
2
3 <h:outputLabel value="#{msg['app.nombre']}" for="nombre"/>
4 <h:inputText value="#{contactoFormBean.nombre}" id="nombre" />
5
6 <h:commandButton value="#{msg['app.saludar']}" />
<h:outputText value="#{contactoFormBean.saludo}" />
7</h:form>
8

No olvidar tener en el archivo mensajes.properties las claves necesarias:

1app_title=Mi primera aplicación JSF


2app.formulario=Directorio de contacto
3app.nombre=Nombre
app.saludo=Hola
4app.saludar=Saludar
5

Lo ejecutamos, y veremos en acción..!

... yyyy...... se ve feo, ¿cierto?


Vemos un "null" que no debería mostrarse hasta que tenga valor, también
vemos que está en una sola línea.

Vamos a arreglarlo.

4. Arreglando la interfaz de usuario

Primero, vamos a poner el botón más abajo. Pero no usemos <br/> sino vamos
a ponerlo dentro de una malla.

Envolvemos la etiqueta, el input y el botón en un panelGrid, que tenga dos


columnas:

1<h:panelGrid columns="2">
2 <h:outputLabel value="#{msg['app.nombre']}" for="nombre"/>
3 <h:inputText value="#{contactoFormBean.nombre}" id="nombre" />
4 <h:commandButton value="#{msg['app.saludar']}" />
</h:panelGrid>
5

¿Por qué dos columnas? Pues para que ponga un componente al costado de
otro, y que hayan dos por fila.
Ahora, necesitamos que el mensaje "Hola null" no aparezca, hasta que el
"null" no sea "null". Es decir, hasta que la propiedad "nombre" tenga un valor.

Podrían decir si se usa un "if"; pero con JSF podemos hacer algo más elegante.

Agreguemos el atributo "rendered" a ese tag. Este atributo - que existe en


todos los componentes de JSF - recibe un valor boolean. Si es "true" (por
omisión) mostrará el componente en la interfaz de usuario. Si es "false", no.

1<h:outputText value="#{contactoFormBean.saludo}"
2 rendered="#{contactoFormBean.nombre ne null}" />

El operador ne significa "no equal".

Lo ejecutamos, y veremos cómo funciona:

Antes del clic en el botón:


Después del clic en el botón

Conclusiones
En este primer ejemplo de JSF, no hemos cubierto únicamente hacer un "hola
mundo", sino mostrar algunas características de JSF:

• Declarar un ManagedBean usando la anotación @Named (nuevo en JSF


2.2)
• Usando un archivo de recursos para los mensajes (buena práctica).
• Acomodando la distribución de los componentes usando un <h.panelGrid
/>
• Mostrando un componente usando el atributo rendered
2. NAVEGACIÓN
La navegación en JSF hace fácil la navegación entre páginas y permite manejar
procesamiento adicional que sea necesario al momento de ir entre una página
y otra.

Hay dos tipos de navegación: la implícita y la definida por el usuario.


En este post veremos estos dos con un ejemplo simple.

Para navegar de una página a otra dentro de una aplicación JSF se hacen a
través de los tags <h:commandLink /> y <h:commandButton />. Ambos hacen los
mismo: hacen un "post" redireccionando el destino a otra página, o a la
misma.

Algo importante: Para poder usar bien estos comandos, deben estar dentro de
los tags <h:form> ... </h:form> , sino, no funciona.

Navegación implícita

La navegación implícita consiste en indicarle al tag - mediante el atributo action


- a qué página se direccionará cuando sea ejecutado. Solo deberá ir el nombre
del archivo sin la extensión. Es por ello que se llama "implícito".

Nuestro archivo mensajes.properties

1abrir_pagina_1_enlace=Este enlace abrirá la página 1


2abrir_pagina_1_texto=Esta es la página 1

Nuestro index.xhtml:

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:h="http://xmlns.jcp.org/jsf/html"
5 xmlns:f="http://xmlns.jcp.org/jsf/core">
6 <h:head>
7 <title>Facelet Title</title>
<f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
8
9 </h:head>
10 <h:body>
11 <h:form>
12 <h:commandLink value="#{msg.abrir_pagina_1_enlace}" action="pagina1" />
13
</h:form>
14 </h:body>
15</html>
16
17

Notemos en la línea 13 (además de usar la buena práctica que vimos en el


anterior post), el action solo indica el nombre del archivo a mostrar. No incluye
la extensión.

Y nuestro archivo pagina1.xhtml

1
2 <?xml version='1.0' encoding='UTF-8' ?>
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4 <html xmlns="http://www.w3.org/1999/xhtml"
5 xmlns:h="http://xmlns.jcp.org/jsf/html"
6 xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
7 <title>Facelet Title</title>
8 <f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
9 </h:head>
10 <h:body>
<h1>#{msg.abrir_pagina_1_texto}</h1>
11 </h:body>
12</html>
13

Navegación definida por el usuario

Este tipo de navegación se hace usando un archivo de configuración, tal como


faces-config.xml usando reglas en formato xml. La estructura de la regla es
bastante simple:
• Se indica en qué página se ejecutará la regla
• Qué comando y orden se ejecutará
• Qué página se mostrará.

Creemos los enlaces en la página de inicio (index.xhtml)

1
<li>#{msg.navegacion_definida_usuario}
2 <ul>
3 <li><h:commandLink action="page1" value="#{msg.pagina1_enlace}" /></li>
4 <li><h:commandLink action="page2" value="#{msg.pagina2_enlace}" /></li>
</ul>
5</li>
6

Y crearemos dos páginas más navegacion_definida_usuario_pagina1.xhtml y


navegacion_definida_usuario_pagina2.xhtml

1
2 <?xml version='1.0' encoding='UTF-8' ?>
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5 <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
6 xmlns:f="http://xmlns.jcp.org/jsf/core">
7 <h:head>
8 <f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
<title>#{msg.navegacion_definida_usuario}</title>
9 </h:head>
10 <h:body>
11 <h1>#{msg.navegacion_definida_usuario}</h1>
12 <h2>#{msg.pagina1}</h2>
13
<h:form>
14 <h:commandButton value="#{msg.ir_inicio}" action="index" />
15 </h:form>
16 </h:body>
17</html>
18

1 <?xml version='1.0' encoding='UTF-8' ?>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
5 <h:head>
6 <f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
7 <title>#{msg.navegacion_definida_usuario}</title>
8 </h:head>
<h:body>
9 <h1>#{msg.navegacion_definida_usuario}</h1>
10 <h2>#{msg.pagina2}</h2>
11
12 <h:form>
13 <h:commandButton value="#{msg.ir_inicio}" action="index" />
</h:form>
14 </h:body>
15</html>
16
17
18

Sí, el único fue el titulo y el nombre físico del archivo.


Ahora bien ¿cómo se le dice que cuando se llame a page1 vaya a
navegacion_definida_usuario_pagina1.xhtml. Aquí es donde entrar el archivo de
configuración. Si lo ejecutamos tal cual, nos aparecerá un mensaje de
advertencia.

Para ello, crearemos un nuevo archivo (Ctrl+N) y en la categoría "JavaServer


Faces" seleccionamos "JSF Faces Configuration".
Clic en "Next" y clic en "Finish" aceptando todas las opciones por omisión.

Se nos abrirá en el editor el archivo en formato xml. Hagamos clic en el barra


superior sobre el botón "PageFlow".

Ahora sí!, algo visual en NetBeans!


Desde aquí podemos definir la navegación desde index.html hasta las demás
páginas. Veamos en el nodo que representa a index.html hay un icono cuadrado
azul en el lado derecho. Hagamos clic allí y arrastremos la flecha hacía las
páginas navegacion_definida_usuario_pagina1.xhtml y
navegacion_definida_usuario_pagina2.xhtml

Y veremos que cada arco tiene un nombre: case1 y case2. Hagamos clic
derecho en cada uno de ellos y seleccionemos "Rename..." para cambiarle el
nombre a page1 y page2 respectivamente.
Adicionalmente, podemos seleccionar el botón superior "Source"

1
2 <?xml version='1.0' encoding='UTF-8'?>
3 <faces-config version="2.2"
4 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
6 http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
7 <navigation-rule>
8 <from-view-id>/index.xhtml</from-view-id>
<navigation-case>
9 <from-outcome>page1</from-outcome>
10 <to-view-id>/navegacion_definida_usuario_pagina1.xhtml</to-view-id>
11 </navigation-case>
12 <navigation-case>
<from-outcome>page2</from-outcome>
13 <to-view-id>/navegacion_definida_usuario_pagina2.xhtml</to-view-id>
14 </navigation-case>
15 </navigation-rule>
16</faces-config>
17

... pero es más rápido (y más bonito) con la parte visual, cierto? :)
Ahora, ejecutamos la aplicación, y probamos los enlaces.

Navegación dependiente de un valor


Hasta este momento, las navegaciones eran en "duro", ya que están definidas
hacía donde tiene que navegar. Pero lo más complejo - y lo más común - es
que después de un procesamiento se decida a qué página hacer. Y este
procesamiento lo haremos usando un ManagedBean.

Para hacer que nuestra aplicación decida a qué página debe direccionarse,
bastará con hacer métodos que devuelvan una cadena y que no tengan
parámetros en el ManagedBean. Lo que devuelva esos métodos pueden ser el
nombre de la regla de navegación descrita en el faces-config.xml o pueden ser la
ruta y nombre de los .xhtml que debería mostrar.

Vamos a hacer un ejemplo que demuestre esto. Haremos un enlace que llame
a un método de un ManagedBean, este identificará la hora actual, y
dependiendo de la hora redireccionará a una página que diga "buenos días", o
"buenas tardes", o "buenas noches" dependiendo del caso.

Este es nuestro ManagedBean

1 package com.apuntesdejava.jsf.controladores;
2
3 import java.util.Calendar;
import java.util.logging.Logger;
4 import javax.enterprise.context.RequestScoped;
5 import javax.inject.Named;
6
7 /**
*
8 * @author dsilva
9 */
10@Named("navegacionBean")
11@RequestScoped
public class NavegacionBean {
12
13 static final Logger LOGGER = Logger.getLogger(NavegacionBean.class.getName());
14
15 public NavegacionBean() {
16 LOGGER.info("Iniciando Bean");
17 }
18
public String saludar() {
19 int hora = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
20 if (hora < 12) {
21 return "buenos-dias";
}
22 if (hora < 18) { //aquí en Lima, a partir de las 7pm ya es oscuro
23 return "buenas-tardes";
24 }
25 if (hora < 23) {
return "buenas-noches";
26 }
27 return null; //todo action=null redirecciona a la misma página de donde fue invocado
28 }
29}
30
31
32
33
34
35

Nuestro faces-config.xml va a recibir esas respuestas y redireccionará a cada


página según corresponda.

1 <?xml version='1.0' encoding='UTF-8'?>


2 <faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
5 http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
6 <navigation-rule>
<from-view-id>/index.xhtml</from-view-id>
7 <navigation-case>
8 <from-outcome>page1</from-outcome>
9 <to-view-id>/navegacion_definida_usuario_pagina1.xhtml</to-view-id>
</navigation-case>
10 <navigation-case>
11 <from-outcome>page2</from-outcome>
12 <to-view-id>/navegacion_definida_usuario_pagina2.xhtml</to-view-id>
13 </navigation-case>
<navigation-case>
14 <from-outcome>buenos-dias</from-outcome>
15 <to-view-id>/saludo/maniana.xhtml</to-view-id>
16 </navigation-case>
17 <navigation-case>
<from-outcome>buenas-tardes</from-outcome>
18 <to-view-id>/saludo/tarde.xhtml</to-view-id>
19 </navigation-case>
20 <navigation-case>
<from-outcome>buenas-noches</from-outcome>
21 <to-view-id>/saludo/noche.xhtml</to-view-id>
22 </navigation-case>
23 </navigation-rule>
24</faces-config>
25
26
27
28
29

Y crearemos estos saludos en nuestro proyecto. Crearemos tres archivos


llamados maniana.xhtml,tarde.xhtml y noche.xhtml. Por orden, los puse dentro de la
subcarpeta saludo. Aquí solo mostraré uno de esos archivos, porque los demás
serán iguales:

1
2 <?xml version='1.0' encoding='UTF-8' ?>
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
5 xmlns:h="http://xmlns.jcp.org/jsf/html"
6 xmlns:f="http://xmlns.jcp.org/jsf/core">
7 <h:head>
<f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
8
<title>Facelet Title</title>
9 </h:head>
10 <h:body>
11 <h1>#{msg.saludo_maniana}</h1>
<h:form>
12 <h:commandButton value="#{msg.ir_inicio}" action="/index" />
13 </h:form>
14
15 </h:body>
16</html>
17

Y... cuando se invoca desde la página de inicio, se deberá llamar al managedBean


seguido del método.

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:h="http://xmlns.jcp.org/jsf/html"
5 xmlns:f="http://xmlns.jcp.org/jsf/core">
6 <h:head>
<f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
7 <title><h:outputText value="#{msg.app_title}"/></title>
8
9 </h:head>
10 <h:body>
11 <h1><h:outputText value="#{msg.app_title}"/></h1>
12
<h:form>
13 #{msg.navegacion_procesada}
14
15 <h:commandLink value="#{msg.saludar}" action="#{navegacionBean.saludar}"/>
16 </h:form>
</h:body>
17</html>
18
19
20

Y listo! Probemos y veamos la magia!

Antes de terminar...

Los nombres de las reglas de navegación pueden ser cualquiera, a gusto del
desarrollador. Pero hay una lista de nombres comunes en las aplicaciones:

• success
• failure
• login
• no results
3. Ciclo de vida de una aplicación
En la página 7.6 The Lifecycle of a JavaServer Faces Application se puede ver el
siguiente gráfico que representa el ciclo de vida de una aplicación:

... pero como no quiero dar una simple traducción de otra web, lo que
haremos es ver cada fase del ciclo de vida en una aplicación.
Fijémonos en los recuadros azules, son seis. Estos son los pasos que se ejecuta
en el lado del servidor. Los recuadros blancos son como los "estímulos" para
que la aplicación haga algo. Como podemos ver en el flujo, puede pasar desde
los primeros recuadros "Restore View" y "Apply Request" hasta "Process
Validations" si es que se aplica una validación, o puede ir directo a la última
fase "Render Response" si es que no tiene nada que hacer. Esto último sucede
cuando se llama a una página por primera vez y no se procesa nada, más que
mostrar el contenido de la página.

Para probar eso, vamos a crear una aplicación (que yo la llamé jsf-03-lifecycle)
y tiene los siguientes .xhtml

index.xhtml

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:h="http://xmlns.jcp.org/jsf/html"
5 xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head >
6 <title>Facelet Title</title>
7 </h:head>
8 <h:body >
9 <h1>Formulario de prueba</h1>
10 <h:form>
<h:panelGrid columns="2">
11 <h:outputLabel value="Nombre" for="nombre"/>
12 <h:inputText id="nombre" value="#{lifecycleBean.personaForm.nombre}" />
13
14 <h:outputLabel value="Sexo" />
15 <h:panelGroup layout="block">
16 <h:selectOneRadio value="#{lifecycleBean.personaForm.sexo}">
<f:selectItem itemLabel="Hombre" itemValue="H" />
17 <f:selectItem itemLabel="Mujer" itemValue="M" />
18 </h:selectOneRadio>
19 </h:panelGroup>
20
21
</h:panelGrid>
22 <h:commandButton action="result" value="Enviar" />
23 </h:form>
24 </h:body>
25</html>
26
27
28
29

result.xhtml

1
2
<?xml version='1.0' encoding='UTF-8' ?>
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5 <html xmlns="http://www.w3.org/1999/xhtml"
6 xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
7 <title>Facelet Title</title>
8 </h:head>
9 <h:body>
10 <h2>Result</h2>
<h:form>
11 <h:panelGrid columns="2">
12 <h:outputText value="Nombre"/>
13 <h:outputText value="#{lifecycleBean.personaForm.nombre}" />
14
<h:outputText value="Sexo"/>
15 <h:outputText value="#{lifecycleBean.personaForm.sexo}" />
16
17 </h:panelGrid>
18 <h:commandLink value="Regresar" action="index"/>
19 </h:form>
</h:body>
20</html>
21
22

Nuestro ManagedBean tendrá el siguiente código:

1 //Archivo LifecycleManagedBean.java
2
3 package com.apuntesdejava.jsf.controladores;
4
5 import com.apuntesdejava.jsf.bean.PersonaForm;
6 import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
7
8 @Named("lifecycleBean")
9 @RequestScoped
10
11public class LifecycleManagedBean {
12
private PersonaForm personaForm = new PersonaForm();
13
14 public LifecycleManagedBean() {
15
16 }
17
18 public PersonaForm getPersonaForm() {
19 return personaForm;
}
20
21 public void setPersonaForm(PersonaForm personaForm) {
22 this.personaForm = personaForm;
23 }
24
25}
26
27
28

El bean que nos servirá de bean tiene el siguiente código:

1 //Archivo PersonaForm.java
2 package com.apuntesdejava.jsf.bean;
3
4 import java.util.Date;
5
public class PersonaForm {
6
7 private String nombre;
8 private char sexo;
9 private Date fechaRegistro;
10 private String correoElectronico;
11
12 public String getNombre() {
return nombre;
13 }
14
15 public void setNombre(String nombre) {
16 this.nombre = nombre;
17 }
18
public char getSexo() {
19 return sexo;
20 }
21
22 public void setSexo(char sexo) {
23 this.sexo = sexo;
}
24
25 public Date getFechaRegistro() {
26 return fechaRegistro;
27 }
28
public void setFechaRegistro(Date fechaRegistro) {
29 this.fechaRegistro = fechaRegistro;
30 }
31
32 public String getCorreoElectronico() {
33 return correoElectronico;
}
34
35 public void setCorreoElectronico(String correoElectronico) {
36 this.correoElectronico = correoElectronico;
37 }
38
39}
40
41
42
43
44
45

Hasta aquí no hay nada extraordinario. Ahora debemos agregar un "oidor"


que lo engancharemos al ciclo de vida para ver los efectos que sucede cuando
ejecutamos la aplicación.

1
2
3
//Archivo MiPhaseListener.java
4 package com.apuntesdejava.jsf.controladores;
5
6 import java.util.Enumeration;
7 import java.util.logging.Level;
8 import java.util.logging.Logger;
import javax.faces.context.FacesContext;
9 import javax.faces.event.PhaseEvent;
10import javax.faces.event.PhaseId;
11import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;
12
13public class MiPhaseListener implements PhaseListener {
14
15 static final Logger LOGGER = Logger.getLogger(MiPhaseListener.class.getName());
16
17 @Override
18 public void afterPhase(PhaseEvent event) {
LOGGER.log(Level.INFO, "Después:{0}", event.getPhaseId());
19 }
20
21
22 @Override
23 public void beforePhase(PhaseEvent event) {
24 LOGGER.log(Level.INFO, "Antes:{0}", event.getPhaseId());
}
25
26 @Override
27 public PhaseId getPhaseId() {
28 return PhaseId.ANY_PHASE;
}
29}
30
31
32

Y para engancharlo al ciclo de vida de la aplicación, debemos crear el archivo


faces-config.xml en WEB-INF. Si no existe el archivo, lo creamos desde la opción
New File > JavaServer Faces > JSF Faces Configuration:

Una vez creado, registramos el listener en el .xml que acabamos de crear


usando el siguiente código:

1<?xml version='1.0' encoding='UTF-8'?>


2<faces-config version="2.2"
3 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
5http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
6 <lifecycle>
7 <phase-listener> com.apuntesdejava.jsf.controladores.MiPhaseListener</phase-listener>
8 </lifecycle>
</faces-config>
9

Ahora, ejecutaremos la aplicación, y veamos el log de cada petición que


hagamos.

Veamos: La primera vez que ejecutamos:


Solo se presentan dos fases: RESTORE_VIEW y RENDER_RESPONSE. Es decir, la
primera y la última fase.

Veamos qué pasa cuando colocamos valores y le damos clic en "Enviar":

Al parecer ya pasa por todas las fases del ciclo de vida. Veamos si ponemos
una validación. Para ello agregamos las líneas resaltadas en el index.html como
sigue:

<?xml version='1.0' encoding='UTF-8' ?>


1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
4 xmlns:f="http://xmlns.jcp.org/jsf/core">
5 <h:head >
6 <title>Facelet Title</title>
7 </h:head>
8 <h:body >
<h1>Formulario de prueba</h1>
9 <h:form>
10 <h:panelGrid columns="2">
11 <h:outputLabel value="Nombre" for="nombre"/>
12 <h:inputText id="nombre" value="#{lifecycleBean.personaForm.nombre}" />
13
14 <h:outputLabel value="Correo electrónico" for="email"/>
<h:inputText id="email" value="#{lifecycleBean.personaForm.correoElectronico}" >
15
<f:validator validatorId="emailValidator"/>
16 </h:inputText>
17
18 <h:outputLabel value="Sexo" />
19 <h:panelGroup layout="block">
<h:selectOneRadio value="#{lifecycleBean.personaForm.sexo}">
20 <f:selectItem itemLabel="Hombre" itemValue="H" />
21 <f:selectItem itemLabel="Mujer" itemValue="M" />
22 </h:selectOneRadio>
23 </h:panelGroup>
24
25
</h:panelGrid>
26 <h:commandButton action="result" value="Enviar" />
27 </h:form>
28 </h:body>
29</html>
30
31
32
33
34

Y creamos la siguiente clase:

1 //EmailValidator.java
2 package com.apuntesdejava.jsf.validation;
3
4 import java.util.regex.Matcher;
5 import java.util.regex.Pattern;
import javax.faces.application.FacesMessage;
6 import javax.faces.component.UIComponent;
7 import javax.faces.context.FacesContext;
8 import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
9 import javax.faces.validator.ValidatorException;
10
11@FacesValidator("emailValidator")
12public class EmailValidator implements Validator {
13
14 private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\." + "[_A-Za-z0-9-]+)*@[A-Za-
z0-9]+(\\.[A-Za-z0-9]+)*" + "(\\.[A-Za-z]{2,})$";
15 private final Pattern pattern;
16 private Matcher matcher;
17
18 public EmailValidator() {
19 } pattern = Pattern.compile(EMAIL_PATTERN);
20
21 @Override
22 public void validate(FacesContext context, UIComponent component, Object value) throws
23ValidatorException {
matcher = pattern.matcher(value.toString());
24 if (!matcher.matches()) {
25 FacesMessage msg = new FacesMessage("Falló la validación del correo electrónico.", "El
26formato de correo electrónico no es válido");
27 msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
28 }
29
30 }
31
32}
33
34
35

Y al ejecutar el proyecto, ingresemos un valor no válido para el correo


electrónico. Esto es lo que nos mostrará en el log del servidor:
(He limpiado el log antes de ejecutar el error. Esto es para poder más
claramente el log que responde). Como podemos ver, no muestra el paso 4 ni
el paso 5. Es decir, no está actualizando el modelo, ni está dando la ejecución
a la aplicación, ya que la validación no lo dejó.

De esta manera, nos podemos asegurar que cada fase se ejecuta de manera
independiente, y no afecta con la lógica de nuestra aplicación. Si queremos
validar una entrada de un campo, debemos asegurarnos de que se haga en su
capa respectiva (usando los validadores) y no cuando haya guardado los
valores en el modelo, ya que sería más difícil el manejo de mensajes de error
al usuario. Con esto en mente, veremos poco a poco como este ciclo de vida
nos ayudará en las siguientes aplicaciones.
4. Facelets (Parte I)
Los facelets es una declaración bastante ligera de declaración de páginas web.
En los JSP, el lenguaje era Java dentro de los JSP, y estos fragmentos se
llamaban scriptlets. En cambio, en JSF, se llama facelets.

El formato por omisión de los facelets es XHTML. Así, las extensiones de los
archivos son de extensión .xhtml.

Como todo xhtml, tiene etiquetas que definen el contenido, pero no cualquier
etiqueta. Estas se obtienen de una biblioteca de etiquetas. Aquí menciono el
conjunto de etiquetas, y las rutas de cada una de ellas.

Biblioteca
Prefij
de URI Ejemplo Contenido
o
etiqueta
Biblioteca Etiquetas
ui:component
de http://xmlns.jcp.org/jsf/facelets ui:
ui:insert
para
Facelets plantillas
Etiquetas
para todos
h:head los
Biblioteca h:body
http://xmlns.jcp.org/jsf/html h:
h:outputText
componente
de HTML
h:inputText s de tipo
UICompone
nt
Biblioteca
Etiquetas
para
JSF
elementos http://xmlns.jcp.org/jsf p: p:type
compatibles
compatibl
para HTML5
es
Atributos
Biblioteca
http://xmlns.jcp.org/jsf/passthrou JSF
para jsf: jsf:id
gh compatibles
atributos
para HTML5
Biblioteca
Prefij
de URI Ejemplo Contenido
o
etiqueta
compatibl
es
Etiquetas de
Biblioteca c:forEach
http://xmlns.jcp.org/jsp/jstl/core c:
c:catch
Core JSTL
JSTL Core
1.2
Biblioteca fn:toUpperCa Etiquetas de
de http://xmlns.jcp.org/jsp/jstl/functi se
fn:
fn:toLowerCa
Funciones
funciones ons
se JSTL 1.2
JSTL

Plantillas con Facelets


Los que hemos desarrollado aplicaciones web vemos que la necesidad de que
todas las páginas luzcan igual (con la misma cabecera, mismo menú y mismo
pie) es algo obligatorio. Y repetir constantemente la misma declaración en
cada página es una mala práctica que es castigado con pena de muerte. Hacer
fragmentos de las partes repetibles y llamarlas en cada página es una salida,
pero no es muy práctica; ya que se tiene (igual) repetir la inclusión de esos
archivos en cada página, y si falta una sola invocación, ya no es lo mismo.

En el viejo Struts, incluía el framework Tiles que permitía crear plantillas


generales, y cuando se invocaba a esa declaración, ya incorporaba las partes
que se deseaban añadir.

Pero ahora, con los Facelets, será mucho más rápido e intuitiva la
implementación de las plantillas.
Así que haremos nuestro primer ejemplo de plantillas con facelets, pero para
hacerlo más interesante, usaremos una plantilla muy útil: BootStrap.com

Utilizando bootstrap.com en una aplicación JavaServer


Faces
Comenzaremos por crear una aplicación JSF común y corriente usando
nuestro NetBeans IDE. Ahora, vamos a agregar el bootstrap a nuestra
aplicación de la siguiente manera:

1. Hacemos clic derecho en el ícono del proyecto y seleccionamos


"Properties".
2. En el margen izquierdo seleccionamos "JavaScript Files"
3.

4. Escribimos en la casilla "Available" el texto twi. Con esto, aparecerá una


lista filtrada de todas las bibliotecas JavaScript que tengan ese nombre.
Seleccionamos el twitter-bootstrap.
5. Lo agregamos a la lista de seleccionados, y le damos clic en "ok".
6. Vemos que ha creado toda una estructura de carpetas para guardar los
estilos (.css) y los javascript (.js)

Ya tenemos la biblioteca del BootStrap. Ahora, vamos a crear la plantilla. En


esta plantilla se invocará a esta biblioteca a fin de que todas las páginas usen
esta misma declaración.

Tenemos dos maneras de crear la plantilla: a través del asistente de NetBeans,


o a mano. Lo bueno de la primera forma es que nos ahorra escribir todos los
tags, lo malo es que nos crea archivos que no vamos a necesitar. Así que
usaremos que lo haremos a mano.

1. Crearemos un nuevo archivo (Ctrl+N), seleccionamos en Categorías


"JavaServer Faces" y el tipo de archivo "JSF Page". Clic en Next.
2. El nombre del archivo será "bootstrap" y lo ubicaremos en "WEB-
INF/pages/templates"

Clic en Finish

Ahora sí, escribiremos el siguiente contenido

1 <!DOCTYPE html>
2
3 <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
4 xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
5 xmlns:h="http://xmlns.jcp.org/jsf/html">
6
7 <h:head>
8 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="#{request.contextPath}/js/libs/twitter-bootstrap/css/bootstrap.css"
9 type="text/css" rel="stylesheet"/>
10 <link href="#{request.contextPath}/js/libs/twitter-bootstrap/css/bootstrap-theme.css"
11type="text/css" rel="stylesheet"/>
12 <h:outputStylesheet name="./css/style.css"/>
<title>Plantilla BootStrap</title>
13 </h:head>
14
15 <h:body>
16 <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
17 <div class="navbar-header">
18 <button type="button" class="navbar-toggle" data-toggle="collapse" data-
19target=".navbar-collapse">
20 <span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
21 <span class="icon-bar"></span>
22 <span class="icon-bar"></span>
23 </button>
24 <a class="navbar-brand" href="#">JSF Templates</a>
</div>
25 <div class="collapse navbar-collapse">
26 <ul class="nav navbar-nav">
27 <li class="#{fn:startsWith(request.pathInfo,'/index')?'active':'' } ">
<h:link outcome="index" value="Home"/></li>
28 <li class="#{fn:startsWith(request.pathInfo,'/about')?'active':'' } "><h:link
29outcome="about" value="About"/></li>
30 <li class="#{fn:startsWith(request.pathInfo,'/contact')?'active':'' } "><h:link
31outcome="contact" value="Contact"/></li>
</ul>
32 </div><!--/.nav-collapse -->
33 </div>
34 </div>
35 <div class="container">
<ui:insert name="content">Content</ui:insert>
36 </div>
37
38 </h:body>
39 <script src="//code.jquery.com/jquery-1.10.2.min.js"></script>
40 <script src="#{request.contextPath}/js/libs/twitter-bootstrap/js/bootstrap.js"></script>
41
42</html>
43
44
45
46

Ya tenemos nuestra plantilla, usando el BootStrap de Twitter, además que


obtenemos el jquery del CDN de JQuery.

Usando la plantilla
Para usar esta plantilla será muy fácil. Crearemos tres archivos en la carpeta
"Web Pages", se deberán llamar index.html, contact.xhtml y about.xml. Estos deben
existir porque la plantilla está llamando a estas páginas a través del tag <h:link
/>. Aquí mostraré algunos ejemplos de cada contenido de estos archivos.

/index.html

<?xml version='1.0' encoding='UTF-8' ?>


<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
1
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 <ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
3 template="/WEB-INF/pages/templates/bootstrap.xhtml"
4 xmlns="http://www.w3.org/1999/xhtml">
5
<ui:define name="content">
6 <h1>Inicio</h1>
7 <p>
8 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et diam varius elit sollicitudin
9 tempus. Etiam feugiat ut arcu ut viverra. Vestibulum magna risus, mattis vel auctor et, porta sed
sem. Maecenas sagittis, orci quis accumsan luctus, massa sem tristique neque, eu ullamcorper
10magna purus at nisi. Nunc dapibus ultrices erat, eu vulputate lorem ultricies vitae. Fusce auctor
11lorem vel leo hendrerit ultricies. Aliquam magna nisi, molestie sodales mauris non, suscipit
12lacinia sapien. Duis ante neque, lacinia vitae magna sit amet, placerat facilisis odio. Suspendisse
13sem sem, iaculis sed gravida sit amet, faucibus eu est. Cras neque eros, aliquam et felis vitae,
condimentum pharetra metus. Maecenas id interdum diam. Maecenas nisi justo, iaculis vitae
14scelerisque interdum, suscipit id justo. Praesent porta, elit non posuere tristique, turpis metus
rutrum enim, et aliquam nisl quam quis nulla. Fusce sagittis, augue ut interdum laoreet, sem felis
vulputate velit, nec dignissim diam odio ut ipsum. Nullam malesuada sollicitudin augue
condimentum dictum. Quisque sit amet blandit tellus.
</p>
</ui:define>

</ui:composition>

/contact.html

<?xml version='1.0' encoding='UTF-8' ?>


<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
1 template="/WEB-INF/pages/templates/bootstrap.xhtml"
2 xmlns="http://www.w3.org/1999/xhtml">
3
<ui:define name="content">
4 <h1>Contact</h1>
5 <p>Vestibulum et velit sit amet felis iaculis congue. Integer ut orci ac diam congue porttitor
6 ac vitae nisl. Nulla volutpat auctor imperdiet. Morbi vitae elit ut leo tempus consectetur eu quis
7 libero. Aliquam sit amet adipiscing justo. Quisque in ligula nec tortor pharetra laoreet. Sed
euismod, est et fringilla vestibulum, nisl augue accumsan urna, eget gravida magna augue a
8 arcu. Sed et dolor urna. Pellentesque vestibulum, orci ac vulputate pharetra, metus nunc
9 condimentum lacus, sit amet consectetur felis augue et metus. Nulla tincidunt nunc at venenatis
10cursus. Phasellus dolor ante, lacinia sed accumsan mattis, adipiscing quis lorem. Phasellus vel
11mollis magna, non luctus dolor. Nunc sem enim, interdum laoreet pharetra consequat, facilisis a
nibh. </p>
</ui:define>

</ui:composition>

/about.html

<?xml version='1.0' encoding='UTF-8' ?>


1 <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 <ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
4 template="/WEB-INF/pages/templates/bootstrap.xhtml"
xmlns="http://www.w3.org/1999/xhtml">
5
6 <ui:define name="content">
7 <h1>About</h1>
<p>Etiam dictum massa et justo auctor euismod. Duis id tortor sit amet leo iaculis
8 consequat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac
9 turpis egestas. Nulla mauris erat, molestie nec leo vel, tempor tempus leo. Quisque arcu erat,
10volutpat a sapien eget, mollis accumsan ante. Vivamus semper interdum nunc, ac accumsan
11ipsum. Cras vitae ultrices velit. Praesent vehicula quam velit. Donec auctor est at fermentum
dignissim. Phasellus facilisis leo dolor, quis ullamcorper metus sagittis vel. Nullam in eleifend
ante, pharetra dignissim orci. Etiam fringilla lorem eu rhoncus tempor. </p>
</ui:define>

</ui:composition>

Ejecutamos la aplicación y listo, ya tenemos un ejemplo de plantilla. Vean lo


simple que es usar la plantilla: solo lo llamamos, y llenamos el contenido que
falta.
5. Facelets - Componentes compuestos (Parte II)
Esta característica de JSF permite hacer lo siguiente: darnos la facilidad de crear nuestro
propio componente utilizando otros componentes. Por ejemplo, si siempre vamos a
seleccionar un producto dependiendo de una selección del tipo de producto, pues sería
conveniente tener un componente que permita al usuario seleccionar los dos datos, y no
nosotros tener que repetir la misma lógica de selección de objetos.

Comenzaremos por crear nuestro componente desde el NetBeans. Después de haber


creado nuestro proyecto web JSF, creamos un archivo y dentro de la categoría "JavaServer
Faces" seleccionamos "JSF Composite Component"

Clic en "Next". El IDE nos va a proponer el nombre (out) y la ubicación. Solo cambiemos el
nombre de nuestro componente. Lo llamaremos "SelectProd"
Clic en "Finish".

Y listo, nos mostrará una plantilla del componente. Tiene dos secciones: la interfaz, y la
implementación.
En la interfaz se describen cuáles son las propiedades que serán de interfaz entre el
formulario y nuestro componente. Como necesitamos dos propiedades para nuestro
selector, lo declaramos allí:

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
5
xmlns:h="http://xmlns.jcp.org/jsf/html"
6
xmlns:f="http://xmlns.jcp.org/jsf/core">
7
8 <!-- INTERFACE -->
9 <cc:interface>
10 <cc:attribute name="productCode" required="true" />
11 <cc:attribute name="product" required="true" />
12
13 </cc:interface>
14
15 <!-- IMPLEMENTATION -->
16 <cc:implementation>
17 </cc:implementation>
</html>
18

Y en la implementación es donde realmente se crea la parte visual de nuestro componente.

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
5
xmlns:h="http://xmlns.jcp.org/jsf/html"
6
xmlns:f="http://xmlns.jcp.org/jsf/core">
7
8
<!-- INTERFACE -->
9 <cc:interface >
10 <cc:attribute name="productCode" required="true" />
11 <cc:attribute name="product" required="true"/>
12 </cc:interface>
13
14 <!-- IMPLEMENTATION -->
15 <cc:implementation>
16 <h:panelGrid columns="2" captionClass="alignRight,alignLeft">

17 <h:outputLabel value="Product code" for="productCode" />


18 <h:selectOneMenu value="#{cc.attrs.productCode}" id="productCode">

19 <f:selectItems value="#{cc.allProductCodes }"

20 var="prodCod"

21 itemLabel="#{prodCod.description}"

itemValue="#{prodCod.prodCode}"/>
22
<f:ajax render="product" />
23
</h:selectOneMenu>
24
25
<h:outputLabel value="Product" for="product" />
26
<h:selectOneMenu id="product" value="#{cc.attrs.product}" >
27
<f:selectItems value="#{cc.products}"
28
var="prod"
29
itemLabel="#{prod.description}"
30
itemValue="#{prod.productId}"/>
31
</h:selectOneMenu>
32
33 </h:panelGrid>
34 </cc:implementation>
35</html>
36

Analicemos:

El <h:panelGrid> es fácil de comprender qué es. También el <h:outputLabel/>.


También podemos ver el <h:selectOneMenu/> que permite mostrar una lista de elementos
como el tag <select> de html. SOLO QUE el atributo "value" está apuntando a un atributo
del componente:
Esto nos permitirá grabarlo en un atributo del componente y manipularlo para lo que
queramos.

La clase del componente


Si lo ejecutamos tal cual, nos mostrará algo bonito y que puede ser enlazado al
ManagedBean que queramos. Pero lo que queremos es mostrar dos combos, y que el
segundo dependa del primero. Por tanto, necesitamos jalar los datos de una tabla para
mostrar los datos en el primer combo, luego cuando se cambie el valor del primer combo,
usando ajax, recarguemos el segundo combo.

Para enviar los datos del primer combo, lo que debemos hacer es usar el atributo
valueChangeListener del tag h:selectOneMenu.Normalmente usamos un ManagedBean para
colocar nuestros métodos listener, y para guardar los valores del formulario. Recordemos
que estamos creando un componente, por tanto, no debemos guardarlo en el
ManagedBean ¿cómo lo haremos? Crearemos la clase del componte.

Crearemos la clase llamada SelectProduct con la anotación @FacesComponent y que


extienda a javax.faces.component.UINamingContainer

1 package com.apuntesdejava.javaee7.jsf.cc.component;
2
3 import java.util.logging.Level;
4 import java.util.logging.Logger;
5 import javax.faces.component.FacesComponent;
6 import javax.faces.component.UINamingContainer;
7
@FacesComponent("SelectProduct")
8
public class SelectProductComponent extends UINamingContainer {
9
10
private static final Logger LOG = Logger.getLogger(SelectProductComponent.class.getName());
11
12
13
}
14

El nombre colocado en la anotación es la que debemos establecerlo en la declaración del


componente en el .xhtml

Además, debemos crear los métodos necesarios para:

• Obtener todos los registros de la tabla PRODUCT_CODE de la base de datos


• Interceptar el cambio de valor del combo productCode
• Obtener todos los registros de la tabla PRODUCT dependiendo del valor
seleccionado en el combo productCode
Aquí muestro la clase completa (y documentada, claro)

package com.apuntesdejava.javaee7.jsf.cc.component;
1
2
import com.apuntesdejava.javaee7.jsf.cc.bean.Product;
3
import com.apuntesdejava.javaee7.jsf.cc.bean.ProductCode;
4
import com.apuntesdejava.javaee7.jsf.cc.dao.ProductCodeDao;
5
import com.apuntesdejava.javaee7.jsf.cc.dao.ProductDao;
6
import java.util.List;
7
import java.util.logging.Level;
8
import java.util.logging.Logger;
9
import javax.faces.component.FacesComponent;
10import javax.faces.component.UINamingContainer;
11import javax.faces.event.ValueChangeEvent;
12import javax.inject.Inject;
13
14/**
15 *
16 * @author dsilva
17 */
18@FacesComponent("SelectProduct")
19public class SelectProductComponent extends UINamingContainer {
20
private static final Logger LOG = Logger.getLogger(SelectProductComponent.class.getName());
21
22
@Inject
23
private ProductCodeDao productCodeDao; //manejamos todos los productCode de la base de
24datos
25 @Inject
26 private ProductDao productDao; //manejamos todos los product de la base de datos
27
28 /**
29 * Obtiene todos los productCode de la base de datos

*
30
* @return
31
*/
32
public List<ProductCode> getAllProductCodes() {
33
return productCodeDao.getAllProductCodes();
34
}
35
36
/**
37
* Obtiene todos los productos de un productCode seleccionado previamente
38
*
39 * @return
40 */
41 public List<Product> getProducts() {
42 //así obtenemos el atributo guardado en el atributo del componente

43 String productCode = (String) getAttributes().get("productCode");

44
45 LOG.log(Level.INFO, "ProductCode seleccionado:{0}", productCode);

46 return productDao.getProductsByProductCode(productCode); //obtiene todos los productos


segun el productCode selecionado
47
}
48
49
/**
50
* Interpreta el cambio del valor del combo ProductCode
51
*
52 * @param event

53 */

54 public void doProductCodeChange(ValueChangeEvent event) {


55 String productCode = (String) event.getNewValue(); //obtenemos el valor...

getAttributes().put("productCode", productCode); //... y lo guardamos en los atributos


56
}
57
58
}
59
60
61

Nota: Aquí estoy usando algo de CDI, para poder instanciar los DAO. Es solo usado para este
ejemplo, no es obligatorio usarlo en los proyectos con componentes compuestos. Se
pueden utilizar EJB, JPA o cualquier fuente de datos. Yo lo usé así por la simpleza aplicada a
este proyecto. En otro post me gustaría hablar más del CDI, y de JPA. Los códigos para los
DAO lo pueden ver en la descarga del proyecto, o aquí mismo:
https://bitbucket.org/apuntesdejava/tutorial-jsf/src/tip/jsf-05-
cc/src/main/java/com/apuntesdejava/javaee7/jsf/cc/dao/jdbc/

Ahora, necesitamos implementarlo en nuestro .xhtml

1 <?xml version='1.0' encoding='UTF-8' ?>

2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"


"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
5
xmlns:h="http://xmlns.jcp.org/jsf/html"
6 xmlns:f="http://xmlns.jcp.org/jsf/core">
7
8 <!-- INTERFACE -->
9 <cc:interface componentType="SelectProduct">
10 <cc:attribute name="productCode" required="true" />

11 <cc:attribute name="product" required="true"/>

12 </cc:interface>

13
14 <!-- IMPLEMENTATION -->

15 <cc:implementation>

16 <h:panelGrid columns="2" captionClass="alignRight,alignLeft">

<h:outputLabel value="Product code" for="productCode" />


17
<h:selectOneMenu value="#{cc.attrs.productCode}"
18
id="productCode"
19
valueChangeListener="#{cc.doProductCodeChange}">
20
<f:selectItems value="#{cc.allProductCodes }"
21
var="prodCod"
22
itemLabel="#{prodCod.description}"
23
itemValue="#{prodCod.prodCode}"/>
24
<f:ajax render="product" />
25 </h:selectOneMenu>
26
27 <h:outputLabel value="Product" for="product" />
28 <h:selectOneMenu id="product"
29 value="#{cc.attrs.product}" >
30 <f:selectItems value="#{cc.products}"
31 var="prod"

32 itemLabel="#{prod.description}"

33 itemValue="#{prod.productId}"/>

34 </h:selectOneMenu>

35
36 </h:panelGrid>
37 </cc:implementation>

38 </html>
39

En las líneas 20,21 y 31 se están usando los métodos creados en nuestra clase
componente. Notemos que siempre se antepone el código cc
La línea 25 muestra el ajax que hace recargar el contenido del segundo combo. Para más
ejemplos de AJAX con JSF, pueden revisar estos posts:

• Ajax en JSF 2.0 - Ejemplo 1: Combo cambia texto


• Ajax en JSF 2.0 - Ejemplo 2: Tabla actualizada según se escriba

... y un botón al componente

Además, podemos agregar un botón a nuestro diseño, y hacerlo de tal manera que cuando
se reutilice el componente, le demos la posibilidad al desarrollador que pueda hacer algo
después con los valores. Para nuestro ejemplo colocaremos un botón y que la etiqueta del
botón sea configurable en la reutilización.

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
5
xmlns:h="http://xmlns.jcp.org/jsf/html"
6 xmlns:f="http://xmlns.jcp.org/jsf/core">
7
8 <!-- INTERFACE -->
9 <cc:interface componentType="SelectProduct">
10 <cc:attribute name="productCode" required="true" />
11 <cc:attribute name="product" required="true"/>
12 <cc:attribute name="buttonText" required="true"/>
13 <cc:attribute name="buttonAction" method-signature="java.lang.String action()" />

14 </cc:interface>
15
16 <!-- IMPLEMENTATION -->
17 <cc:implementation>
<h:panelGrid columns="2" captionClass="alignRight,alignLeft">
18
<h:outputLabel value="Product code" for="productCode" />
19
<h:selectOneMenu value="#{cc.attrs.productCode}"
20
id="productCode"
21
valueChangeListener="#{cc.doProductCodeChange}">
22
<f:selectItems value="#{cc.allProductCodes }"
23
var="prodCod"
24
itemLabel="#{prodCod.description}"
25
itemValue="#{prodCod.prodCode}"/>
26 <f:ajax render="product" />
27 </h:selectOneMenu>
28
29 <h:outputLabel value="Product" for="product" />
30 <h:selectOneMenu id="product"
31 value="#{cc.attrs.product}" >

32 <f:selectItems value="#{cc.products}"

33 var="prod"

34 itemLabel="#{prod.description}"

35 itemValue="#{prod.productId}"/>

36 </h:selectOneMenu>

37
<h:commandButton action="#{cc.attrs.buttonAction}"
38
value="#{cc.attrs.buttonText}"
39
40 />
41 </h:panelGrid>

42 </cc:implementation>
43</html>
44

No hay implementaciones, solo son las declaraciones de lo que tendrá y se mostrará en el


componente.

Usando el componente

Aquí viene lo divertido: utilizar el componente. Para ello, crearemos un ManagedBean al


que llamaremos FormBean y tendrá el siguiente contenido:

1 package com.apuntesdejava.javaee7.jsf.cc.managedbean;
2
3 import com.apuntesdejava.javaee7.jsf.cc.bean.Product;
import com.apuntesdejava.javaee7.jsf.cc.dao.ProductDao;
4
import java.io.Serializable;
5
import java.util.logging.Logger;
6
import javax.enterprise.context.SessionScoped;
7
import javax.inject.Inject;
8
import javax.inject.Named;
9
10
/**
11
*
12
* @author dsilva
13 */
14@Named(value = "formBean")
15@SessionScoped
16public class FormBean implements Serializable {
17
18 private static final Logger LOG = Logger.getLogger(FormBean.class.getName());
19
20 private String productCode;
21 private String product;
@Inject
22
ProductDao productDao;
23
24
/**
25
* Creates a new instance of FormBean
26
*/
27
public FormBean() {
28
}
29
30
public String getProductCode() {
31
return productCode;
32 }
33
34 public void setProductCode(String productCode) {
35 this.productCode = productCode;
36 }
37
38 public String getProduct() {
39 return product;

40 }
41
42 public void setProduct(String product) {
43 this.product = product;

44 }
45
46 /**
47 * Obtiene el objeto del producto seleccionado

*
48
* @return El Objeto {@link com.apuntesdejava.javaee7.jsf.cc.bean.Product}
49
* seleccionado
50
*/
51
public Product getProductSelected() {
52
if (product == null) {
53
return null;
54
}
55
return productDao.findByProductId(product);
56 }
57
58 /**
59 * Ejecuta una acción. En este caso solo va a devolver null para retornar a
60 * la misma página

61 *

62 * @return El nombre de la página a mostrar después de ejecutar la acción

63 */

64 public String showProductSelected() {


65 return null;//que devuelve nada, o sea, se queda en la misma pagina

}
66
}
67
68
69
70

Y crearemos nuestro index.xhtml de la siguiente manera:

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:h="http://xmlns.jcp.org/jsf/html"
5
xmlns:ezcomp="http://xmlns.jcp.org/jsf/composite/ezcomp">
6
<h:head>
7
<title>Facelet Title</title>
8
</h:head>
9
<h:body>
10
<h:messages/>
11
<h:form>
12 <ezcomp:SelectProd productCode="#{formBean.productCode}"
13 product="#{formBean.product}"
14 buttonAction="#{formBean.showProductSelected}"
15 buttonText="Mostrar producto seleccionado"
16 />

17 <h:outputText rendered="#{formBean.productSelected ne null}"

18 value="El producto seleccionado es: #{formBean.productSelected}"/>

19 </h:form>

20 </h:body>
</html>
21

La línea 5 se auto escribe en el IDE cuando se comienza a escribir el tag <ezcomp... y


presionando las teclas Ctrl+Espacio.
Las líneas 12 al 16 son la declaración del uso del componente. Como se puede ver, es total
transparente. No necesitamos saber nada de los combos, ni cómo accede a la base de
datos. Asumimos que está bien, y lo podremos reutilizar en cualquier formulario que
necesitemos.
6. Recursos
En JSF se pueden incluir recursos tales como hojas de estilos (CSS), javascript e
imágenes que queramos usar en nuestra web, pero de una manera ordenada.
En este post veremos cómo lo hace.

A partir de la versión JSF 2.0, los recursos pueden ubicarse en un subdirectorio


bajo una carpeta llamada resources (así, tal cual el nombre) que debería estar
dentro de la raíz del módulo web (donde están todos los archivos web) o bajo
META-INF. Por convención, los componentes de JSF reconoce una de estas dos
ubicaciones.

Los nombres de los directorios de los recursos serán los mismos que se
declaren en el atributo library de los componentes JSF.

Por ejemplo, para nuestros archivos .css lo guardaremos dentro de la carpeta


/resources/css/, lo invocaremos con el tag

7<h:outputStylesheet library="css" name="style.css"/>

Para los javascript que están dentro de la carpeta js, lo invocamos así:

8<h:outputScript library="js" name="jquery-2.1.1.min.js"/>

Las imágenes, si están dentro de la carpeta images se invocaría así:

12<h:graphicImage library="images" name="la_tierra.jpg"/>

Finalmente, tendríamos la siguiente estructura


y el código completo sería este:

1
2
<?xml version='1.0' encoding='UTF-8' ?>
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5 <html xmlns="http://www.w3.org/1999/xhtml"
6 xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
7 <title>Recursos en JSF</title>
8 <h:outputStylesheet library="css" name="style.css"/>
9 <h:outputScript library="js" name="jquery-2.1.1.min.js"/>
</h:head>
10
<h:body>
11 <h:outputText id="info" styleClass="info" value="Esto es un mensaje de información" />
12 <h:graphicImage library="images" name="la_tierra.jpg"/>
13 <script>
$("#info").hover(function(){
14 $(this).fadeOut("slow");
15 $(this).fadeIn("slow");
16 })
17 </script>
</h:body>
18</html>
19
20
7. Resource Library Contracts
¿Qué pasaría si nuestra aplicación web debe lucir con diferentes estructuras de página en
diferentes secciones de la aplicación? Sabemos que podemos usar los facelets, que -
dependiendo de qué plantilla le indiquemos - nos mostrará una estructura diferente. Pero,
si son varias páginas que pertenecen a una carpeta, sería un suicidio poner en todas las
páginas qué plantilla debe utilizar ¿cierto? Aquí es donde aparecen los "Resource Library
Contracts" (no encontré una traducción acorde al español) que consiste en usar una
plantilla especial, si las páginas en cuestión están dentro de una URL específico

Cómo funciona

Recordemos los Facelets: En nuestro cliente de la plantilla, usamos el tag <ui:composition />
y le indicamos en el atributo template cuál es la plantilla a utilizar. Si queremos que una
página utilice otra plantilla, le deberíamos cambiar el valor del atributo template. Pues bien,
para usar los Resource Library Contracts, vamos a hacer que todos los clientes de plantilla
usen la misma plantilla. Al menos vamos a indicarle que están en la misma ubicación. El
truco está en el archivo faces-config.xml. Allí se le indicará, dependiendo del juego de
caracteres de las páginas a mostrar, qué plantilla deberá utilizar.

Ingredientes

Para nuestro ejemplo, usaremos:

• NetBeans 8 (también funciona con la versión 7 y todas sus actualizaciones)


• GlassFish v4
• JDK 7 | JDK 8
• Base de datos - NO

Creando y configurando el proyecto

Por practicidad, los últimos proyectos lo estoy desarrollando con Maven. Por tanto,
crearemos un proyecto Maven > Web Application
Y lo llamaré jsf-07-rlc. Luego, le agregaremos el framework JSF. Para ello, entraremos a las
propiedades del proyecto, y en la categoría "Frameworks", agregamos a "JavaServer Faces"
y nos aseguramos de que sea la versión JSF 2.2.
Clic en "OK"

Creación de las páginas

Crearemos cuatro páginas, dos de ellas estarán en la raíz del módulo web, y las otras dos
estarán en una subcarpeta llamada "app1"

Comenzaremos a editar el archivo /index.xhtml


1 <?xml version='1.0' encoding='UTF-8' ?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:h="http://xmlns.jcp.org/jsf/html"
5
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
6 <h:head>
7 <title>Facelet Title</title>
8 </h:head>
9 <h:body>

10 <ui:composition template="/template.xhtml">

11 <ui:define name="content">

12 <h1>Este es el contenido principal de la aplicación. </h1>

13 <p>Podemos ver las páginas que tienen otra plantilla haciendo clic aquí:

14 <a
href="#{facesContext.externalContext.requestContextPath}/faces/app1/index.xhtml">App</a>
15
</p>
16
</ui:define>
17 </ui:composition>
18 </h:body>
19</html>

y su página hermana page2.xhtml

1 <?xml version='1.0' encoding='UTF-8' ?>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:h="http://xmlns.jcp.org/jsf/html"
5 xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

6 <h:head>

7 <title>Facelet Title</title>

8 </h:head>

<h:body>
9
<ui:composition template="/template.xhtml">
10
<ui:define name="content">
11
<h1>Esta es la página 2. </h1>
12
<p>Aquí tenemos otra página, usando la plantilla por omisión.
13
14
Para entrar a la otra plantilla, debemos regresar a la página principal.
15
16
Hacer clic arriba donde dice "Inicio".
17
</p>
18
</ui:define>
19 </ui:composition>
20 </h:body>
21</html>
22

Notemos que en ambos casos existe el tag <ui:composition template="/template.xhtml"> que


prácticamente dice que hay una plantilla en la raiz.

Ahora, editaremos las páginas de /app1/index.xhtml

1 <?xml version='1.0' encoding='UTF-8' ?>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:h="http://xmlns.jcp.org/jsf/html"
5 xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

6 <h:head>

7 <title>Facelet Title</title>

8 </h:head>

<h:body>
9
10
<ui:composition template="/template.xhtml">
11
<ui:define name="nav">
12
<a href="#{facesContext.externalContext.requestContextPath}">Inicio principal</a> -
13
<a href="page1.xhtml">Página 1</a>
14
</ui:define>
15
<ui:define name="content">
16
<h1>Este es el contenido que se encuentra en la página inicial. </h1>
17
<p>Utiliza otra plantilla. Todo luce diferente, pero el contenido es el mismo
18 </p>
19 </ui:define>
20 </ui:composition>
21 </h:body>
22</html>
23

y aquí está /app1/page1.xhtml

1 <?xml version='1.0' encoding='UTF-8' ?>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:h="http://xmlns.jcp.org/jsf/html"
5 xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
6 <h:head>

7 <title>Facelet Title</title>

8 </h:head>

9 <h:body>

10
<ui:composition template="/template.xhtml">
11
<ui:define name="nav">
12
<a href="#{facesContext.externalContext.requestContextPath}">Inicio principal</a> -
13
<a href="index.xhtml">Inicio</a>
14
</ui:define>
15
<ui:define name="content">
16
<h1>Este es el contenido de otra página que se encuentra en la página inicial. </h1>
17
<p>Así se usan plantillas por páginas. Clic en el menú de arriba para cambiar de página.
18
</p>
19 </ui:define>
20 </ui:composition>
21 </h:body>
22</html>
23

Igual aquí se están asumiendo la misma plantilla "/template", pero ahora veremos que esto
no existe... es solo una ilusión.

Creando el "Contrato"

Primero, vamos a crear la plantilla que se usará para la carpeta app1. Luego, se creará el
que será de omisión. Con ayuda del IDE, seleccionamos File > New > JavaServer Faces > JSF
Resource Library Contract
Clic en "Next"
Nuestro contrato se llamará "app". Notemos que lo creará dentro de la carpeta "contracts".
Es una carpeta predefenida como la de "resources" que vimos en el post anterior.

Activamos el check de "Create Initial Template" para que nos cree una plantilla inicial.
Clic en "Finish".
Veamos la estructura que ha creado.

Vemos la carpeta "contracts", la subcarpeta "app" y dentro está la plantilla y su css.


Notemos, además, que el template.xhtml está en la raiz relativa a "app"

También veamos el template.xhtml creado. Cuenta con dos <ui:insert /> llamados "top" y
"content".
Cambiaremos el nombre del "top" a "nav", ya que contendrá nuestro menú principal

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
5
xmlns:h="http://xmlns.jcp.org/jsf/html">
6
7
<h:head>
8 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
9 <h:outputStylesheet name="./css/default.css"/>
10 <h:outputStylesheet name="./css/cssLayout.css"/>
11 <title>App Template</title>
12 </h:head>
13
14 <h:body>
15
16 <div id="top" class="top">

17 <ui:insert name="nav">Top</ui:insert>

</div>
18
19
<div id="content" class="center_content">
20
<ui:insert name="content">Content</ui:insert>
21
</div>
22
23
</h:body>
24
25
</html>
26

Crearemos otro contract, pero que se llamará "default" y no le pediremos que cree una
plantilla. Con esto, solo nos creará la carpeta dentro de contract. Allí crearemos el archivo
template.xml, que será nuestra nueva raíz.

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
5 xmlns:h="http://xmlns.jcp.org/jsf/html">
6
7 <h:head>
8 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
9 <title>Default Template</title>

10 </h:head>
11
12 <h:body>
13
14 <nav >

<ui:insert name="nav">
15
<h:form>
16
<h:commandLink value="Inicio" action="index"/> |
17
<h:commandLink value="Página 2" action="page2"/>
18
</h:form>
19
</ui:insert>
20
</nav>
21
22
<div id="content" class="center_content">
23
<ui:insert name="content">Content</ui:insert>
24 </div>
25
26 </h:body>
27
28</html>
29

El gestor de contratos - faces-config.xml

Ahora, crearemos el archivo faces-config.xml: File > New > JavaServer Faces > JSF Faces
Configuration
Clic en Next, y con los valores predeterminados, clic en "Finish"
El contenido será el siguiente:

1 <?xml version='1.0' encoding='UTF-8'?>


2 <faces-config version="2.2"
3 xmlns="http://xmlns.jcp.org/xml/ns/javaee"

4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

5 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
6
<application>
7
<resource-library-contracts>
8
<contract-mapping>
9
<url-pattern>/app1/*</url-pattern>
10
<contracts>app</contracts>
11 </contract-mapping>
12 <contract-mapping>
13 <url-pattern>*</url-pattern>
14 <contracts>default</contracts>

15 </contract-mapping>

16 </resource-library-contracts>

17 </application>
18</faces-config>

El primer contract (línea 8 al 11) define que todas las páginas que estén dentro de /app1/*
usen el contrato app,

8 <contract-mapping>
9 <url-pattern>/app1/*</url-pattern>

10 <contracts>app</contracts>
11</contract-mapping>

y el siguiente contrato indica que todos los demás <url-pattern>*</url-pattern>(a manera de


un default de switch case, es decir, si no se cumple lo de arriba, se aplica el último) se
utilizará el contrato default

12<contract-mapping>
13 <url-pattern>*</url-pattern>
14 <contracts>default</contracts>
15</contract-mapping>

Proyecto en ejecución

Veamos cómo luce cuando se ejecuta. Esta es la página principal, con sus dos enlaces
arriba, y el enlace de abajo que nos lleva a las páginas de /app1

Esta es la /index.xhtml
Cuando hacemos clic en "Pagina 2" de arriba, nos lanza esto

Regresamos a la página "Inicio" y ahora le damos clic en "App"

Y cuando hacemos clic en el menú de arriba en "Pagina 1", nos muestra esto
8. HTML5
En las versiones anteriores a JSF 2.2, solo se podía usar etiquetas compatibles con HTML 4,
y las etiquetas y atributos de HTML5 se estaban volviendo muy útiles y necesarias para las
aplicaciones. Así que decidieron que el JSF deba contemplar HTML5. Veremos en qué
consiste.

HTML5 tiene nuevas características bastantes útiles. Si quieres conocer un poco más de
HTML5, te recomiendo que veas este tutorial que está muy bueno: HTML 5 Intro. Ahora
bien, es posible que estas características (input de tipo fecha, tag de vídeo y audio nativo,
base de datos en el navegador y más) quieras implementarlas en un JSF que tiene etiquetas
establecidas. JSF sale a nuestra salvación, ya que la tecnología permite dos maneras de
implementación:

• Pase de elementos (Passthrough Elements)


• Pase de atributos (Passthrough Attributes).

Pase de elementos

El pase de elementos consiste en que podemos usar etiquetas y atributos HTML5 y a la vez
tratarlos con características que son propias de JSF. Para ello, necesitamos poner un
namespace al documento JSF y usar los atributos de JSF en las etiquetas HTML pero con el
prefijo del namespace. Quedaría algo así como un HTML con chispitas de sabor de JSF:

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:h="http://xmlns.jcp.org/jsf/html"
5
xmlns:jsf="http://xmlns.jcp.org/jsf"
6 >
7 <h:head>
8 <title>Usando paso de elementos</title>
9 </h:head>
10 <h:body>
11 <h1>Usando paso de elementos</h1>

12 <form jsf:id="form">

13 <label jsf:for="nombre">Nombre</label>

<input required="required"
14
name="nombre"
15
jsf:id="nombre"
16
type="text"
17
value="#{formBean.nombre}"/>
18
19
20
<label jsf:for="anioNacimiento">Año que naciste</label>
21
<input required="required"
22
name="anioNacimiento"
23
jsf:id="anioNacimiento"
24
type="number"
25 value="#{formBean.anioNacimiento}"/>
26
27
28
29 <h:commandButton value="Calcular edad" action="paso-elementos-resp" />
30
31
32
33 <h:link outcome="index" value="Regresar"/>
34 </form>
35 </h:body>
36</html>
37

Notar que con solo colocar el atributo jsf:id el tag HTML5 tiene las características como JSF.
Por eso podemos utilizar atributos de HTML5 (como type="number", required y más)
conjuntamente con funcionalidades JSF

Pase de atributos

Esto es al revés: tenemos tags de JSF y queremos adornarlo con atributos propios de
HTML5. El namespace a usar debe ser

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:h="http://xmlns.jcp.org/jsf/html"
5
xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
6
<h:head>
7
<title>Usando paso de atributos</title>
8
</h:head>
9
<h:body>
10
<h1>Usando paso de atributos</h1>
11 <h:form>
12
13 <h:outputLabel value="Nombre"
14 for="nombre"
15 p:contextmenu="menu"/>

16 <h:inputText id="nombre"

17 value="#{formBean.nombre}"

18 p:placeholder="Escribe tu nombre..." />

19 Nota: el menú contextual del LABEL solo funciona en Firefox


20
21
22 <h:outputLabel value="Fecha nacimiento"

23 for="fecNac" />

24 <h:inputText value="#{formBean.fechaNacimiento}"

25 id="fecNac"

p:required="required"
26
p:type="date"/>
27
Nota: El selector de fecha solo funciona en Chrome, Opera y Safari.
28
29
30
<h:commandButton action="paso-atributos-resp" value="Calcular"/>
31
32
33
<h:link outcome="index" value="Regresar"/>
34
35
</h:form>
36
<menu type="context" id="menu">
37
<menuitem label="HOLA!"/>
38
<menuitem label="Este es una opción del menú"/>
39 </menu>
40 </h:body>
41</html>
42
9. Lenguaje de expresiones
Esta vez veremos el Lenguaje de Expresiones, o también conocido como EL (Expression
Language).

El EL es usado en varias tecnologías de JavaEE, tales como JSF, JSP y CDI. Además, lo
podemos encontrar en entornos stand-alone. Lo que veremos ahora solo es cuando se
ejecuta en contenedores Java EE.

Las expresiones nos permite acceder a los datos de los componentes JavaBean. Como
sabemos, un JavaBean (no confundir con Enterprise JavaBean) debe tener el siguiente
formato:

• Las propiedades deben accederse usando los métodos con prefijo set para poner un
valor y get para obtener un valor. Para los valores boolean se puede utilizar get o is,
el resultado es el mismo.
• Después del prefijo, debe comenzar el nombre con mayúscula
• Los métodos set solo deben tener un único parámetro
• Los métodos get no deben tener ningún parámetro.
• Los métodos son public

Ejemplo:

1 public void setNombre(String nombre){


2 //...
3 }
4 public String getNombre(){
5 //...
6 }
7 public boolean isActivo(){
8 //...

9 }

Y para accederlo desde un EL, simplemente se utiliza el nombre sin el prefijo, pero
comenzando con minúsculas
1<c:if test="#{sessionScope.persona.activo}">
2 Hola #{sessionScope.persona.nombre}
3...
4</c:if>

Para abreviar, el EL proporciona una manera simple - usando expresiones - para realizar las
siguientes tareas:

• Leer dinámicamente datos almacenados en los componentes JavaBeans, diversas


estructuras de datos y objetos implícitos
• Escribir dinámicamente datos, tales como entradas de formularios a componentes
JavaBeans.
• Invocar arbitrariamente métodos públicos y estáticos.
• Realizar operaciones aritméticas, lógicas y de cadena.
• Construir colecciones de objetos y realizar operaciones en colecciones.

Sintaxis de evaluación de expresiones

Existen dos maneras para evaluar una expresión:

• De manera inmediata
• De manera diferida.

La manera inmediata se hace cuando se necesita que el valor del componente bean se
escriba directamente cuando construye la página. Para ello se utiliza la sintaxis ${}

1<c:out value="${persona.nombre}" />

La manera diferida se utiliza cuando el valor a obtener debe pasar por todo el ciclo de vida.
Se pueden utilizar para:

• Leer y escribir datos


• Utilizar expresiones de método
1<h:inputText value="#{persona.nombre}" />
2<h:outputText value="#{persona.calcularRenta(tasa.ingreso)}" />

Referenciando expresiones

Para mostrar o manejar las expresiones de objetos, se hace simplemente (como se vió
hasta ahora) con la declaración tal cual
#{persona.nombre}

también podemos invocar a la misma propiedad usando corchetes:


#{persona["nombre"]}

Con esto, como podrás imaginar, podrías invocar a cada propiedad de una manera
dinámica
#{persona[campoActual]}

Donde campoActual es una variable de tipo String que contiene el nombre del campo a
mostrar.
#{ valor == objeto.campo }

Si tenemos una clase enum:

1public enum Estado {


2 activo,enProceso,suspendido,terminado
3}

.. también podemos invocarlo desde la expresión:


#{ estado == estado.activo }

Los arreglos también se pueden acceder usando un índice entre corchetes


#{cliente.pedidos[1]}

También podemos manipular mapas (java.util.Map). Supongamos que la propiedad pedidos


es un java.util.Map con el key de tipo String, y queremos acceder al pedido con key
"teclado". Estas dos líneas hacen lo mismo:
#{cliente.pedido["teclado"]}
#{cliente.pedido.teclado}

Es decir, manipula los maps como si fueran propiedades de un objeto.

Lambda

También podemos usar las expresiones Lambda. A pesar de que son parte de Java SE 8, se
pueden usar en expresiones EL de Java EE 7 con Java SE 7.

Para probar las expresiones lambda en Java SE debemos agregar la biblioteca respectiva a
nuestro proyecto. Por ello - para efectos de este ejemplo - agregaremos la siguiente
dependencia a nuestro proyecto Maven.

67
<dependency>
68 <groupId>org.glassfish</groupId>
69 <artifactId>javax.el</artifactId>
70 <version>[3.0-b03,)</version>
71</dependency>
72<dependency>
73 <groupId>javax</groupId>
74 <artifactId>javaee-api</artifactId>
75 <version>7.0</version>
76 <scope>provided</scope>
</dependency>
77

Y usaremos el paquete javax.el.*. Para nuestro ejemplo usaremos el siguiente código:

88void eval(String input) {


89 System.out.println("\n====");
90 System.out.println("Cadena EL: " + input);
91 Object ret = elp.eval(input); //aquí evaluamos el contenido
92 System.out.println("Valor obtenido: " + ret);
93}

Las expresiones Lambda usan el operador flecha (->). Los identificadores que se encuentran
a la izquierda del operador son llamados parámetros lambda. El cuerpo, que se encuentra a
la derecha del operador, pueden ser expresiones EL. Los parámetros lambda pueden estar
envueltos en signos de paréntesis, y pueden ser omitidos si solo tienen un parámetro.
x -> x+1
(x,y) -> x + y
() -> 64

Las expresiones lambda pueden ser utilizadas como una función, y se pueden invocar
inmediatamente:
((x,y)-> x + y)(3,4)

.. e imprime 7

También se pueden usar en conjunción con un asignamiento, separados por un punto y


coma (;)
v = (x,y) -> x + y; v(3,4)

v es la función lambda creada, y después se invoca con v(3,4)


Operaciones con colecciones

EL permite operaciones de colecciones de objetos: conjuntos (set), listas y mapas. Permite


la creación dinámica de colecciones de objetos, los cuales pueden ser manipulados por
streams y pipelines.
Al igual que las expresiones lambda, las operaciones sobre colecciones de objetos son parte
de Java SE 8, pero podemos usar estas expresiones EL en Java EE 7 sobre Java SE 7.

Veamos: así creamos un par de conjuntos (set):

{1,2,3}
{"Ana","Beto","Carlos"}

Para construir una lista, es como sigue... además podemos hacer que una lista contenga
diferentes tipos:

["Abraham","Bart","Carl"]
[1,"dos",[tres,cuatro]]

Las variables tres y cuatro deben existir

59void run() {
60 eval("nums={1,2,3}");
61 eval("noms={'Ana','Beto','Carlos'}");
62 eval("noms2=['Abraham','Bart','Carl']");
63 eval("tres=3;cuatro='4'");
64 eval("lista=['Abraham','Bart','Carl']");
65 eval("lista2=[1,'dos',[tres,cuatro]]");
66
67}
Se mostro el EL en un Java SE, pero la idea es mostrar en un JSF. Aquí va el ejemplo. Primer
código:

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:h="http://xmlns.jcp.org/jsf/html">
5 <h:head>
6 <title>Ejemplos de EL</title>
7 </h:head>
8 <h:body>

9 <h1>Ejemplos de EL</h1>

10 ${lista= [512,128,65] }

11 </h:body>
</html>
12

Y cuando se ejecuta, aparece...

Todo bien, todo normal. Ahora con la ayuda de nuestro super IDE NetBeans, a la variable
que acabamos de crear vamos a ordenarlo. Vean qué pasa cuando escribimos el nombre de
la variable lista seguido del punto (.)
Automáticamente, el IDE sabe que ese objeto es una lista y nos mostrará las propiedades
de esa lista.... y de sus objetos

Por tanto, ejecutamos el código:

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:h="http://xmlns.jcp.org/jsf/html">
5 <h:head>
6 <title>Ejemplos de EL</title>
7 </h:head>

8 <h:body>

9 <h1>Ejemplos de EL</h1>

10 ${lista= [512,128,65].stream().sorted().toList() }

</h:body>
11
</html>
12

Y el resultado es lo esperado:

Operadores

Son los clásicos que conocemos

• Aritméticos: +, -, *, /, div, %, mod, - (signo negativo)


• Concatenación de cadenas: +=
• Lógicos: and, &&, or, ||, not, !
• Relacional: ==, eq, !=, ne, <, lt, >, gt, <=, ge, >=, le
• Si es vacío: El operador empty determina si una lista está vacía o un objeto es nulo
• Condicional: A ? B : C (Lo mismo que en Java SE)
• Lambda: ->(ya lo conocemos)
• Asignación: =
• Punto y coma: ;
10. Converters, Listeners, y Validators
Recordemos que:

• Los converters son usados para convertir que es recibida desde un componente de
entrada (como el inputText).
• Los Listeners son usados para que escuchen los eventos que sucedan en una página
para realizar acciones definidas.
• Los validators son usados para validar que el dato que es recibido por un
componente de entrada cumpla con los requisitos necesarios antes de que sea
procesado en la aplicación.

Usando Converters estándar

La impelementación de JSF proporciona un conjunto de implementaciones de Converters


que podemos usar para convertir datos. Su función principal es tomar una cadena que
viene desde el API del Servlet y convertirlo a objetos Java para nuestra aplicación.

Las clases de los converters están en el paquete javax.faces.convert. Cada converter está
asociado a un mensaje de error, por si falla la conversión. Si se le pide que convierta una
cadena en un número, el JSF ya tiene preparado el mensaje de error a mostrar.

Si quieren ver cuáles son los mensajes de errores predeterminados de GlassFish, pueden
revisar el archivo $GLASSFISH_HOME/modules/javax.faces.jar

Aquí una pequeña muestra del contenido del archivo.


Vamos a probar el uso de un converter estándar.
Recordemos algo: todas las entradas desde un input son siempre texto (String), por tanto,
nuestro converter debe tener la capacidad de convertirlo a número antes que llegue al
ManagedBean.

En nuestro proyecto de ejemplo, haremos que guarde un campo de tipo numérico llamado
"edad".

1 package com.apuntesdejava.jsf.converters;
2
3 import java.io.Serializable;
4 import javax.enterprise.context.SessionScoped;
5 import javax.inject.Named;
6
7 /**
8 *
9 * @author dsilva
10 */
11@Named
12@SessionScoped
public class FormBean implements Serializable{
13
14
private Integer edad;
15
16
public Integer getEdad() {
17
return edad;
18
}
19
20
public void setEdad(Integer edad) {
21
this.edad = edad;
22
}
23
24
}
25

Ahora, pondremos nuestro inputText que guarde ese valor, pero con el converter (que está
resaltado en el siguiente código)

1 <?xml version='1.0' encoding='UTF-8' ?>


2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:h="http://xmlns.jcp.org/jsf/html"
5 xmlns:f="http://xmlns.jcp.org/jsf/core">
6 <h:head>
7 <title>Facelet Title</title>

8 </h:head>

9 <h:body>

10 <h1>Probando converters</h1>

<h:form>
11
<h:outputLabel value="Escribir la edad:"/>
12
<h:inputText value="#{formBean.edad}">
13
<f:converter converterId="javax.faces.Integer" />
14
</h:inputText>
15
</h:form>
16
</h:body>
17
</html>
18

Al ejecutarlo, y escribir un texto (y presionar Enter) el formulario tratará de evaluarlo antes


de guardarlo, y si no es un número, lanzará el mensaje de error:

Así se ingrese un número decimal, mostrará el mismo mensaje de error, porque el valor no
es un número entero. En cambio, si escribimos un número entero, y presionamos Enter,
entonces no mostrará ningún mensaje de error (y no va a mostrar nada, porque no le
hemos puesto nada de mensajes)
Notemos que el tag tiene el atributo converterId con un ID. Para ver la lista de todos los ID
de converter que corresponde a cada clase converter, lo podemos encontrar aquí: Table
11-1 Converter Classes and Converter IDs

Ahora hagámoslo más interesante (y es para resolver una pregunta repetitiva que me han
hecho): Cómo convertir un inputText en una fecha (java.util.Date). Pues, casi lo mismo. Solo
que en este caso, usaremos el tag <f:converterDateTime />

Primero, agregaremos a nuestro ManagedBean el atributo de tipo java.util.Date (ojo, es


fecha, no String):

1 package com.apuntesdejava.jsf.converters;
2
import java.io.Serializable;
3
import java.util.Date;
4
import javax.enterprise.context.SessionScoped;
5
import javax.inject.Named;
6
7
/**
8
*
9
* @author dsilva
10
*/
11
@Named
12@SessionScoped
13public class FormBean implements Serializable{
14
15 private Integer edad;
16 private Date fechaRegistro;
17
18 public Integer getEdad() {
return edad;
19
}
20
21
public void setEdad(Integer edad) {
22
this.edad = edad;
23
}
24
25
public Date getFechaRegistro() {
26
return fechaRegistro;
27
}
28
29
public void setFechaRegistro(Date fechaRegistro) {
30 this.fechaRegistro = fechaRegistro;
31 }
32
33}
34
35

Ahora, hagamos el inputText, pero para que no vean que hago trampa, haremos que el
mismo valor que se ingrese, se vuelva a imprimir, pero con otro formato... total, si es tipo
java.util.Date puede moldearse a cualquier formato:
1 <?xml version='1.0' encoding='UTF-8' ?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
<html xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:h="http://xmlns.jcp.org/jsf/html"
5
xmlns:f="http://xmlns.jcp.org/jsf/core">
6 <h:head>
7 <title>Facelet Title</title>
8 </h:head>
9 <h:body>

10 <h1>Probando converters</h1>

11 <h:form>

12 <h:panelGrid columns="2">

13 <h:outputLabel value="Escribir la edad:"/>

<h:inputText value="#{formBean.edad}">
14
<f:converter converterId="javax.faces.Integer" />
15
</h:inputText>
16
17
<h:outputLabel value="Fecha registro:"/>
18
<h:inputText value="#{formBean.fechaRegistro}">
19
<f:convertDateTime pattern="dd/MM/yyyy" />
20
</h:inputText>
21
22
<h:outputText value="#{formBean.fechaRegistro}">
23
<f:convertDateTime dateStyle="full" locale="es"/>
24
</h:outputText>
25 </h:panelGrid>
26 <h:commandButton value="Entrar"/>
27 </h:form>
28 </h:body>
29</html>
30

Para el inputText notemos que el converter tiene de formato dd/MM/yyyy. Lo que significa
que se solo va a convertir en formato fecha si es que tiene ese patrón: día (dos dígitos),
mes (dos dígitos), año (cuatro dígitos) separados por el signo de la barra inclinada ("/"). Una
vez convertido, cuando se recargue la página, se mostrará con el formato indicado en la
línea 24: fecha completa en idioma español.

Este sería el resultado, si ingreso una fecha válida:

Para conocer más sobre el formato de fechas, podemos revisar la lección del Tutorial Java:
Customizing Formats.

Y para el tratamiento de monedas, podemos usar el tag convertNumber y podemos indicar


el tipo de moneda a usar, y la plataforma se encargará de convertirlo.

28<h:outputLabel value="Precio total"/>


29<h:outputText value="#{formBean.precioTotal}">
30 <f:convertNumber currencyCode="USD" type="currency"/>
31</h:outputText>

Por ejemplo, si la propiedad precioTotal fuera un Double con el valor 123.456, este sería el
resultado:

Más sobre el formato de números, podemos revisar la lección del Tutorial Java:
"Customizing Formats".

Registrando Listeners en Componentes

Los Listeners son métodos de ManagedBean o clases (que implementa a


javax.faces.event.ValueChangeListener) que nos permite interceptar ciertos eventos antes de
que llegue el requerimiento al ManagedBean. Por ejemplo, podemos interceptar cada vez
que hay un cambio en el valor del componente
Veamos esta clase:
1 package com.apuntesdejava.jsf.listeners;
2
3 import java.util.logging.Level;
4 import java.util.logging.Logger;
5 import javax.faces.event.AbortProcessingException;
6 import javax.faces.event.ValueChangeEvent;
7 import javax.faces.event.ValueChangeListener;
8
9 /**
10 *
11 * @author dsilva
*/
12
public class CambiaNombreChangeListener implements ValueChangeListener {
13
14
private static final Logger LOG =
15Logger.getLogger(CambiaNombreChangeListener.class.getName());
16
17 @Override
18 public void processValueChange(ValueChangeEvent event) throws AbortProcessingException {
19 LOG.log(Level.INFO, "Entrando al Listener de {0}", getClass().getName());

20 if (event.getNewValue() != null) {

21 LOG.log(Level.INFO, "\tNuevo valor:{0}", event.getNewValue());

22 }

23 }
24
25}
Además, agreguemos la propiedad nombre al ManagedBean

16
public class FormBean implements Serializable{
17
private static final Logger LOG = Logger.getLogger(FormBean.class.getName());
18
19 private Integer edad;
20 private Date fechaRegistro;
21 private Double precioTotal=123.456;
22
23 private String nombre;
24
25 public String getNombre() {
26 return nombre;

27 }
28
29 public void setNombre(String nombre) {
LOG.log(Level.INFO, "Nuevo valor para la propiedad Nombre:{0}", nombre);
30
this.nombre = nombre;
31
}
32

Ahora bien, este será nuestra página modificada:

33 <h:outputLabel value="Escribe tu nombre:"/>


34 <h:inputText value="#{formBean.nombre}" required="true">
35 <f:valueChangeListener
type="com.apuntesdejava.jsf.listeners.CambiaNombreChangeListener" />
36
37 </h:inputText>
</h:panelGrid>

Cuando escribamos un valor en el campo "nombre" y hagamos clic en "Entrar", el resultado


en el log será el siguiente:

Usando Validators estándard

Los validadores son componentes que permiten validar una entrada de texto que tenga
cierta estructura. Existen cinco predefinidos, y estos son las clases y tags que se utilizan
para usar correctamente un validador:

Clase Tag Función

BeanValidator validateBean Registra un validador para el componente

Verifica que el valor a ingresar esté dentro de un


DoubleRangeValidator validateDoubleRange rango determinado. El valor debe ser un punto
flotante o convertible a punto flotante.

Verifica que la longitud del valor ingresado en el


componente tenga máximo un determinado
LengthValidator validateLength
valor. El valor a ingresar en el componente debe
ser una cadena java.lang.String

Verifica que el valor que se ingresa en el


componente deba estar dentro de un rango
LongRangeValidator validateLongRange determinado. El valor puede ser de tipo
numérico o una cadena que pueda ser
convertido a long.
Clase Tag Función

Verifica que el valor ingresado en el componente


RegexValidator validateRegex
coincida con una expresión regular (regex)

Se asegura que se haya ingresado un valor en el


RequiredValidator validateRequired
componente, no permite valores vacíos.

Todas las clases implementan la interfaz Validator. Es decir, también podemos crear
nuestros propios validadores si implementamos dicha interfaz.

Si queremos validar que el valor ingresado debe tener un valor entre 1 y 10:

38<h:outputLabel value="Cantidad" for="cantidad"/>


39<h:inputText id="cantidad" size="4" value="#{formBean.cantidad}">
40 <f:validateLongRange minimum="1" maximum="10"/>
41</h:inputText>
42<h:message for="cantidad"/>

Si ingresamos un valor fuera del rango, nos aparece un mensaje de error:


Y si queremos, también podemos validar - usando regexp - que la cadena ingresada sea un
correo electrónico:

46<h:outputLabel value="Correo electrónico" for="email"/>


47<h:panelGroup>
48 <h:inputText id="email" value="#{formBean.email}">
49 <f:validateRegex pattern="[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}"/>

50 </h:inputText>
51 <h:message for="email" errorStyle="color:red" />
52</h:panelGroup>

También podría gustarte