Documentos de Académico
Documentos de Profesional
Documentos de Cultura
INDICE
1. INTRODUCCION: JAVA EE7 – JSF
2. Navegación
4. Facelets (Parte I)
6. Recursos
8. HTML5
9. Lenguaje de expresiones
• 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".
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"
Clic en "Finish"
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".
1#{msg['app.formulario']}
Hemos comenzado con la capa de Vista. Ahora vamos a darle la vida a esta
vista usando la capa Controladora.
3. Controlador de JSF
Se recomienda usar un Bean por cada vista, ya que serán las controladores de
las páginas.
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.
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
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
Vamos a arreglarlo.
Primero, vamos a poner el botón más abajo. Pero no usemos <br/> sino vamos
a ponerlo dentro de una malla.
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.
1<h:outputText value="#{contactoFormBean.saludo}"
2 rendered="#{contactoFormBean.nombre ne null}" />
Conclusiones
En este primer ejemplo de JSF, no hemos cubierto únicamente hacer un "hola
mundo", sino mostrar algunas características de JSF:
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
Nuestro index.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
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
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
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.
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.
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
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
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
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
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
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
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
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:
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
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
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
Clic en Finish
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
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
</ui:composition>
/contact.html
</ui:composition>
/about.html
</ui:composition>
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í:
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:
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.
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
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
44
45 LOG.log(Level.INFO, "ProductCode seleccionado:{0}", productCode);
53 */
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/
12 </cc:interface>
13
14 <!-- IMPLEMENTATION -->
15 <cc:implementation>
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:
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.
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
Usando el componente
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 *
63 */
}
66
}
67
68
69
70
19 </h:form>
20 </h:body>
</html>
21
Los nombres de los directorios de los recursos serán los mismos que se
declaren en el atributo library de los componentes JSF.
Para los javascript que están dentro de la carpeta js, lo invocamos así:
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
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"
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"
10 <ui:composition template="/template.xhtml">
11 <ui:define name="content">
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>
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
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
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.
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
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.
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
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:
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>
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
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
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:
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
16 <h:inputText id="nombre"
17 value="#{formBean.nombre}"
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:
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:
• 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 ${}
La manera diferida se utiliza cuando el valor a obtener debe pasar por todo el ciclo de vida.
Se pueden utilizar para:
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}
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 }
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
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
{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]]
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:
9 <h1>Ejemplos de EL</h1>
10 ${lista= [512,128,65] }
11 </h:body>
</html>
12
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
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
• 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.
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
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)
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
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 />
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">
<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.
Para conocer más sobre el formato de fechas, podemos revisar la lección del Tutorial Java:
Customizing Formats.
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".
20 if (event.getNewValue() != null) {
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
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:
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:
50 </h:inputText>
51 <h:message for="email" errorStyle="color:red" />
52</h:panelGroup>