JSF Introduccion

http://www.notodocodigo.com/introduccion-a-jsf/ Introducción a JSF Vamos al lío… El ejemplo va a consistir en la típica

Views 97 Downloads 4 File size 1MB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

http://www.notodocodigo.com/introduccion-a-jsf/

Introducción a JSF Vamos al lío… El ejemplo va a consistir en la típica página de envío de mensajes, de estas que nos encontramos como formulario de contacto, nos pedirá en nombre, la dirección de correo, etc… Cuando pulsemos el botón “Aceptar”, nos llevará a una página de confirmación. Si confirmamos el mensaje es enviado y se nos mandará a la página “resultado”. Por el contrario, si decidimos editar el mensaje, nos llevará de vuelta a la página inicial. Puede ser interesante consultar este post, en el que se crea un proyecto Jsf sencillo, de modo que podamos utilizarlo como punto de partida para la creación de nuestros ejemplos.

Preparando Eclipse Antes de empezar, hay que configurar el entorno de desarrollo. Nos bajaremos alguna implementación de jsf de sun, por ejemplo desde http://javaserverfaces.java.net/ Luego en Eclipse vamos a “Windows >> preferentes >> Java >> buid path >> User Libraries”: Nos creamos unas variables de usuario, donde metemos las referencias al jar de jsf que vamos a usar. Yo he creado dos librerías de usuario, una con un jar, y otra con otros dos, que me he bajado de la misma página para probar.

A continuación vamos a las propiedades del proyecto (botón derecho) y localizamos los “Proyect Facets”. Marcamos la casilla de JavaServer faces y configuramos las user libraries que nos interese.

Con esto, cuando añadamos un proyecto JSF al tomcat, sabrá de donde tomar las librerías. Si se configuran correctamente las librerías en Maven, esto no hace falta, pero tampoco viene mal.

Configurando el Web.xml En primer lugar se configura el servlet que va a atender las peticiones jsf:

Faces Servlet javax.faces.webapp.FacesServlet 1

Faces Servlet /faces/*

Es el controlador que se encargará de las peticiones de “facets”, como se puede ver, está mapeado para que intercepte las peticiones bajo el path del mismo nombre. El “context-param” que figura a continuación indica que estamos en el entorno de desarrollo, lo que permitirá mostrar cierta información adicional en el caso de que el nos equivoquemos en algo:

Se configura con los valores: Development, UnitTest, SystemTest, o Production.

javax.faces.PROJECT_STAGE Development

Las páginas a utilizar Como hemos comentado, vamos a necesitar tres páginas: o

mensaje.xhtml → Donde se escriben los datos del mensaje

o

confirmar.xhtml → Donde de muestra los datos y las opciones de enviar o editar

o

resultado.xhtml → Que muestra el mensaje de confirmación

La página “mensaje.xhtml”

Una aplicación simple JavaServer Faces

Escriba a continuación el sus datos y el mensaje a enviar Formulario de envio



Remitente:

Sexo:



Fecha:

(dd/MM/yyyy)

Correo electrónico:

Tipo Mensaje:



Mensaje:







En lo primero en lo que nos tenemos que fijar es en el espacio de nombres de nuestra página xhtml:

El primero es el estándar xhtml y los otros dos permiten usar etiquetas para el core jsf y para elementos html. Hay que tener en cuenta que las etiquetas jsf están pensadas para mostrar componentes en cualquier plataforma, pda, etc… y no solo en navegadores web. En la página tenemos un h:head, un h:body y un h:form. Todos los componentes de un formulario jsf, deben ir dentro de un h:form. A continuación se ha definido una tabla, para colocar los elementos. Vamos a observar uno de ellos:

Se puede ver que es obligatorio (required=”true”), si no se rellena, se mostrará el mensaje en h:message for=”fremitente”. El mensaje de error se puede poner en cualquier sitio del formulario, hace referencia al campo por su etiqueta “fremitente”. En el mensaje de error, se usa lo que se pone en “label”. Lo siguiente en lo que nos tenemos que fijar es en: value=”#{userBean.firstName}”, que enlaza la propiedad del mismo nombre en el bean manejado (managed bean).

¿Qué es un managed bean? Bien, son beans (pojo’s) que se registran en el contexto para que las usen los componentes jsf. Para que una aplicación jsf haga referencia a un bean y a sus métodos y propiedades, la clase debe encontrarse en el classpath y registrada en el “faces-config.xml”, o bien, anotada, como en el siguiente ejemplo:

package com.notodocodigo.model;

import java.util.Date; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.validator.ValidatorException;

@ManagedBean @SessionScoped public class BeanMensaje {

protected String remitente; protected String mensaje; protected Date fecha; protected String sex; protected String email; protected String tipoMensaje = "felicitacion";

//getters y setters de las propiedades... etc. // hay que añadirlos aquí...

public void validaEmail(FacesContext context, UIComponent toValidate, Object value) throws ValidatorException { String correo = (String) value; if( ! correo.toLowerCase().matches( "^[a-z0-9]+([_\\.-][a-z0-9]+)*@([a-z0-9]+([\\.-][a-z09]+)*)+\\.[a-z]{2,}" ) ) { FacesMessage message = new FacesMessage("La dirección de email, no es correcta"); throw new ValidatorException(message); } }

public String enviarMensaje() { FacesMessage mensajeResultado = new FacesMessage("El mensaje ha sido enviado"); String salida = "resultado"; //Si queremos ir al inicio (mensaje.xhtml) hay que poner salida="mensaje" FacesContext.getCurrentInstance().addMessage(null, mensajeResultado); return salida;

} }

La anotación @ManagedBean puede tener un atributo “name”, pero si se omite, usa el nombre de la clase poniendo el primer carácter en minúscula. Hemos visto el campo “remitente”, que se “mapea” con la propiedad del bean. Vamos a ver otros campos. Si queremos un campo “fecha” que se mapee con una propiedad de tipo “Date” en el bean, hay que hacer la conversión:

(dd/MM/yyyy)

Lo que podemos ver es que se usa la función que realiza la conversión aplicando el patrón que se indique, dicha función está entre etiquetas de apertura y cierre de h:inputText. Nos podemos ayudar de eclipse para cargar sugerencias, situando el cursor donde nos interese y pulsando “ctrl. + espacio”:

Aquí vemos otro ejemplo de edición:

Si nos fijamos, en el campo de tupo “email”, se invoca a la función “validaEmail(…)” del bean manejado:



Si se añade una etiqueta en el formulario se nos mostrarán todos los mensajes, vamos a ver un ejemplo del resultado:

He señalado en rojo el lugar donde se muestran todos los mensajes, que son los mismos que los que aparecen en cada uno de los campos. Vamos a fijarnos ahora en el botón del formulario:

Con esto se está indicando que cuando se pulse el botón, si no hay ningún error, se vaya a la página “confirmar.xhtml”. Esto se llama “navegación implícita” y ocurre cuando no se define ninguna regla en el fichero facets-config.xml.

La página “confirmar.xhtml” y “resultado.xhtml” La página es idéntica a “mensaje.xhtml”, contiene un “h:head”, un “h:body” y un “h:form” que contiene una tabla, como en el caso anterior. Las únicas dos diferencias son, que por un lado, los campos tendrán la forma:



Es decir, una salida de texto, que muestra el contenido del bean manejado. La segunda diferencia está en los botones que hay que definir en el formulario:





Creo que se explica sólo. Si pulsamos el botón Editar, nos iremos a “mensaje.xhtml” y si pulsamos el botón “Confirmar” ejecutará la función “enviarMensaje” del bean manejado. Si analizamos la implementación de ese método que pusimos más arriba, se rellena un mensaje y se envía de manera “hardcode” a la página “resultado.xhtml”. La implementación de la página “resultado.xhtml” es la misma que “confirmar.xhtml” pero sin los botones.

Introducción a JSF El ciclo de vida de una petición JSF

El ciclo de vida de una petición JSF En el ciclo de vida de procesamiento de peticiones jsf se realizan todas las tareas de back -end que de otro modo debería realizar el programador. Procesa los parámetros de entrada y gestiona un conjunto de componentes del lado del servidor y los sincroniza para que lo vea el usuario en el navegador. Realiza la mayoría de las operaciones del lado del servidor de una manera automática basada en eventos. Enlaza directamente campos con propiedades de una clase java, de modo que

sincroniza beans del lado del servidor, con los componentes de UI que ve el usuario. Es lo que se conoce como “gestión de estado” y es la piedra angular de JSF. No se almacena el estado, esto es, una transacción entre el cliente y el servidor, no almacena en memoria las transacciones anteriores. Jsf mantiene una vista en el lado del servidor que representa partes importantes del estado actual del cliente. Las fases del ciclo de vida son: o

Crea o restaura la vista → Crea o restaura un árbol de componentes (vista) del lado del servidor, que representan los componentes de interfaz de usuario del cliente. Las vistas se crean y almacenan en un contenedor de vistas llamado “FacesContext”. El desarrollador no se tiene que preocupar por si los datos se mezclan al realizar múltiples “request” ya que cada petición irá en su propio hilo.

o

Aplica los valores del “request” → Actualiza los componentes del lado del servidor con datos “frescos” que provienen del lado del cliente. Esto se hace a través de una invocación a “processDecodes()” en el árbol de componentes (UIViewRoot), lo que provocará que se llame de manera recursiva al método “processDecodes()” de cada uno de los componentes hijo y los métodos “decode()” de los componentes UI. Los componentes UI que tienen el atributo “value”, como los campos de texto, etc., implementarán el interfaz “ValueHolder”, los componentes de formulario que tienen valores editables por el usuario, implementan “EditableValueHolder” y finalmente aquellos componentes que ejecutan acciones, como los botones o los links, implementarán “ActionSource”.

o

Ejecuta las Validaciones → Procesa las validaciones y realiza la conversión de datos en su caso. Para hacer esto invoca al método “processValidators()” de manera recursiva en el árbol de componentes (UIViewRoot). Cualquier componente que falle en la validación o en la conversión tendrá la propiedad “valid = false”

o

Actualiza el modelo → Una vez que se pasan las validaciones y las conversiones, se realizan las actualizaciones de los objetos que modelan los datos en el lado del servidor (Managed Beans). El mecanismo es similar a los anteriores, se invoca al interfaz “processUpdates()” de manera recursiva en el árbol de componentes UIViewRoot

o

Invoca a la lógica de aplicación → En esta fase se invoca a los “action method”, esto es, al algún código que hemos configurado para que realice las acciones necesarias con los datos. Se invoca al método “processApplication()” del UIViewRoot y se transmite eventos en cola a cada componente que implemente “ActionSource”.

o

Devuelve la respuesta → Salva el estado y devuelve la respuesta al cliente para que los componentes sean representados. El mecanismo es similar al que hemos comentado anteriormente, se invocará en cascada a los métodos “encodeXX()” de cada componente. El lenguaje de “renderizado” puede ser cualquiera, html, xml, etc. Se almacena el estado de la vista para que pueda ser utilizado en las siguientes peticiones.

El atributo “immediate” Imaginemos que tenemos el formulario “mensaje.xhtml” que vimos en el artículo anterior ( Introducción a Jsf ), y queremos incluir un botón “Salir”, en la página de envío de mensajes, de modo que independientemente de lo que hayamos introducido en el formulario, se nos redirija a una página “inicio.xhtml”. Si añadimos en el formulario el botón de este modo:

Si lo dejamos simplemente así, tendremos un error, ya que el formulario será enviado y se procesarán los campos de entrada y las validaciones según el ciclo de vida que hemos visto anteriormente. Si añadimos el atributo “immediate” con el valor “true” en un componente UICommand, como un botón, nos saltaremos las validaciones e iremos directamente a la página “inicio.xhtml”. En general, “immediate” disparará los eventos de acción inmediatamente antes de realizar la fase de validación y evitará la actualización de datos en el modelo:

Se puede aplicar el atributo “immediate = true” a los componentes que implementen “EditableValueHolder”, como ocurre con los campos de entrada, para que realicen la validación de manera inmediata. El uso típico de este mecanismo se da cuando queremos que

unos campos del formulario se habiliten en función del contenido que tenga otro campo. Para que esto funcione habrá que configurar “listeners”. Los “phase listeners” permiten ejecutar código java, en distintos puntos de las fases que definen el ciclo de vida. Por ejemplo, se puede mostrar un mensaje en función de los valores que se están introduciendo en tiempo de ejecución. Y esto es tan simple como crear clases Java que implementen el interfaz “PhaseListener” y registrar estas clases de manera programática o a través del archivo “faces-config.xml”. Por otra parte el control de las excepciones se realiza a través de “ExceptionHandler” que manejará las excepciones que se puedan lanzar en el ciclo de vida Jsf. Hasta aquí esta breve introducción sobre el ciclo de vida. En los próximos artículos seguiremos profundizando sobre el diseño da aplicaciones con Jsf y hablaremos sobre los facelets

Facelets Los Facelets han sido creados para reemplazar a las jsp’s como mecanismo para declarar vistas. Lo que se ha querido conseguir es proporcionar un modelo-vista-controlador “limpio” al no incluir código Java en el lenguaje de marca o etiqueta. Si bien, es posible utilizar en una misma aplicación jsp y facelets, es recomendable migrar las páginas jsp y utilizar únicamente Facelets ya que las páginas Jsp no soportan todas las nuevas características JavaServer Faces de la plataforma Java EE 6. En aplicaciones Jsf hacer uso de páginas Jsp se considera deprecado.

¿Qué son los facelets? Es un lenguaje ligero de declaración de páginas para la construcción de vistas JavaServer Faces usando plantillas de estilo HTML y la creación de árboles de componentes. Con las siguientes características: o

Usa XHTML para la creación de páginas web

o

Soporta la librería de etiquetas de facelets, además de Jsf y JSTL

o

Soporta expresiones EL

o

Plantillas para páginas y componentes

o

Reutilización de código, por el uso de plantillas y la composición de componentes

o

Muy rápido de compilar y renderizar, con validaciones en tiempo de compilación

Uso de plantillas Hay dos elementos, los clientes de las plantillas, y los ficheros plantilla. Los clientes de las plantillas son los ficheros que se corresponden con el “viewId”, como por ejemplo “inicio.xhtml”. El cliente empleará uno o más ficheros plantilla que se aplicarán sobre él, de modo que se aplicará un “look and feel” determinado definido en estos ficheros. Vamos a verlo con un ejemplo. Crearemos un fichero llamado “plantilla.xhtml” con el siguiente contenido:



Marcador de posición de título



Marcador de posición del cuerpo de la página

Lo primero que hemos hecho ha sido definir el espacio de nombres para las etiquetas de plantillas de facelets “xmlns:ui=”http://java.sun.com/jsf/facelets”. Nos tenemos que fijar en las etiquetas “ui:insert”, que serán reemplazadas con “algo”, cuando se compile la página. Ahora vamos a modificar el contenido de la página “mensaje.xhtml” que vimos en la introducción a Jsf para que use esta plantilla:



Una aplicación simple JavaServer Faces

Applicaciónn JSF que usa una plantilla

Escriba a continuación sus datos y el mensaje a enviar Formulario de envio





Remitente:

Sexo:



Fecha:

(dd/MM/yyyy)

Correo electrónico:

Tipo Mensaje:



Mensaje:







En el cliente de la plantilla se usa “ui:composition”, cualquier cosa fuera de las etiquetas “ui:define” será ignorada. El resultado sería el siguiente:

Vamos a analizar a continuación las etiquetas “ui”:

ui:composition

Agrupa un conjunto de componentes que pueden ser utilizados en otra página. Todo lo que quede fuera de esta etiqueta será ignorado. Puede envolver a una plantilla o hacer referencia a otra plantilla.

ui:decorate

Es similar al anterior, hace referencia a una plantilla que, en este caso, es obligatoria. Los componentes que queden fuera de esta etiqueta sí serán renderizados a diferencia de lo que ocurre con el caso anterior.

ui:define

Se usan dentro de “ui:composition” y asigna un nombre a las secciones de código (conjuntos de componentes), que serán insertadas en la plantilla mediante el uso de etiquetas “ui:insert” que hagan referencia al mismo nombre.

ui:insert

Se usan dentro de una plantilla para incluir los componentes que se definieron con “ui:define”. Si no se indica in nombre, insertará los elementos contenidos en la etiqueta.

ui:include

Se combina con el uso de la etiqueta “ui:param” para habilitar la inclusión de páginas parametrizadas en otras páginas o plantillas. Por ejemplo, podemos pasar el usuario a una plantilla de este modo:





Donde la plantilla “plantillaDos.xhtml”, podría ser:

Hola, #{miUsuario.nombreCompleto}, bienvenido!!

ui:param

Se usa dentro de “ui:include” y declara pares clave-valor que pueden usarse con expresiones EL (#{}). Aquí termina este artículo, en el siguiente hablaremos sobre los beans manejados. Si te ha gustado, puedes dejar un comentario.

Introducción a JSF Manejando Beans

Manejando Beans Expresiones EL A lo largo de este capítulo haremos referencia a las expresiones EL, que permiten acceder a distintas propiedades de los “Beans” en tiempo de ejecución. Es recomendable repasar lo que comentamos en el artículo sobre SpEL, ya que se puede aplicar a lo que veremos aquí. El uso de expresiones EL nos permitirá modificar el comportamiento de los componentes en tiempo de ejecución. Por ejemplo, una operación típica que podríamos realizar es la siguiente:

Donde “esPersonaFisica” devuelve un valor booleano. Por otro lado existen valores a los que se puede acceder de manera implícita a través de expresiones EL, como son: “application”, “cookie”, “facesContext”, “param”, “request”, “session”, etc. Por ejemplo, para acceder a un parámetro del request, podríamos hacer “#{param[„num‟]}” o simplemente “#{param.num}”, o del mismo modo expresiones anidadas como “#{param[cookie.usuario]}”. Recordar, que como ya vimos en la introducción a jsf, también se pueden invocar a métodos del Managed Bean, como en el siguiente ejemplo, en el que se invoca a un método que envía un correo electrónico:

Si estamos ejecutando la aplicación en un contenedor EE 6 (de no ser así no funcionaría), podríamos pasar argumentos al Managed Bean, por ejemplo, para pasar los parámetros del request “p” y “q” a la función de nuestro bean, podríamos hacer lo siguiente:

#{mbean.miMetodo(param.p, param.q)}

Por temas de seguridad, no es recomendable pasar valores sin comprobar directamente a los métodos, pero nos vale a modo de ejemplo.

Manipulando los Beans Como vimos anteriormente, los beans manejados son POJOs que se declaran de alguna manera en el “runtime” de Jsf. En el ejemplo de aplicación que vimos en la introducción a Jsf, utilizamos el bean manejado “BeanMensaje”, vamos a ver un ejemplo similar:

... import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped;

@ManagedBean @SessionScoped public class BeanMensaje {

@ManagedProperty(value="No todo código") private String mensaje; …

Como se puede ver, está anotado con “@ManagedBean” y tiene el alcance (scope) de sesión. El alcance puede ser el siguiente: o

@NoneScoped → Se instancia por demanda de algún otro managed bean

o

@RequestScoped → Está disponible en el mismo http request

o

@ViewScoped → Sólo disponible en la vista

o

@SessionScoped → Disponible en una sesión http de usuario

o

@ApplicationScoped → Disponible en el ciclo de vida de la aplicación, para todos los usuarios

o

@CustomScoped → Un ejemplo de esto es #{facesContext.attributes}, que es el ámbito recomendado.

Se ha asignado un valor por defecto con la anotación: @ManagedProperty(value=”No todo código”). El nombre del bean define como se puede hacer referencia a él, con expresiones EL. Si no se indica ningún nombre mediante el uso del atributo name, será el nombre de la clase comenzando en minúscula. En el ejemplo anterior, haríamos referencia al bean con el nombre “beanMensaje”. Si quisiéramos asignarle otro nombre, lo indicaríamos de este modo: @ManagedBean(name=”nuevoNombreAUtilizar”) Alternativamente, se pueden definir los beans manejados, de manera equivalente, en el archivo “faces-config.xml” de este modo:

beanMensaje com.notodocodigo.modelo.BeanMensaje session

mensaje No todo código



Como se ve en el ejemplo, se utiliza la clase “BeanMensaje” como un managed bean. Se está utilizando el alcance de sesión. Se recomienda utilizar en la medida de lo posible el alcance de atributos “FacesContext”, que solo está activo durante el ciclo de vida de faces:

#{facesContext.attributes}

Por otra parte, en lo relativo a las clases, se pueden utilizar directamente listas o maps, por ejemplo:



… java.util.ArrayList ...

… java.util.HashMap ….

En general por cada anotación Jsf 2.0, existe su equivalente en Xml para su uso en “facesconfig.xml”. Se recomienda usar anotaciones, ya que es más fácil de mantener. De cualquier manera, cuando editamos el fichero faces-config.xml en Eclipse, podemos ver los elementos que se utilizan en la declaración de un bean manejado (pulsando Ctrl+Espacio):

Si nos fijamos, en la etiqueta “managed-property” hemos indicado el valor por defecto de una propiedad de tipo String. También se pueden inicializar elementos mediante el uso de “listentries” o “map-entries“, por ejemplo:

...

numerosLong

java.lang.Long 1 3 #{masNumeros[0]} #{masNumeros[1]} #{masNumeros[2]}

...

En este ejemplo, se indica con “value-class” que estamos usando una lista de tipo “java.lang.Long”, ya que por defecto, las listas son de tipo String. Como se puede ver en el ejemplo, se hace referencia a los valores de una lista de otro managed bean “masNumeros”, que se habrá definido previamente. De este modo, se pueden obtener los valores de otros managed beans mediante el uso de expresiones EL. Una cosa que hay que tener en cuenta, es que solo se puede hacer referencia a otros managed beans que se hayan definido con scope “none” o con un ámbito mayor o igual que el bean que hace la referencia. Un ejemplo para el caso de los map, con claves de tipo entero, podría ser:

...

provincias

java.lang.Integer

28 Madrid

45 Toledo



...

Como vimos en la introducción a Jsf, para hacer referencia a un atributo del bean en la vista, utilizaríamos expresiones EL, por ejemplo, si queremos sacar en la vista, el valor del atributo mensaje, podríamos hacer: El mensaje es: “#{beanMensaje.mensaje}” Del mismo modo, si utilizamos un componentes UI que acepten un valor de entrada, y que implementen EditableValueHolder, como por ejemplo “h:inputtext“, se actualizará el bean manejado a través de los métodos setter, en general, cuando se haga submit del formulario. También se pueden utilizar expresiones para configurar propiedades que estén accesibles vía EL. Por ejemplo, podemos recuperar propiedades del request y asociárselas a un managed bean:

numeroResultados #{param.num}

Tambíen se puede hacer esto con anotaciones:

@ManaqedProperty(value="#{param.num}") private String numeroResultados;

De este modo, podemos añadir el valor en un campo:

Que se recuperaría como hemos dicho del request, por ejemplo: http://… /pagina.xhtml?num=2324

Acceder por código a los Managed beans Para recuperar por código un valor del managed bean, podemos ejecutar lo siguiente:

ELContext contextoEL = context.getELContext( ); Application appli = context.getApplication( ); String miPropiedad = (String) apli.evaluateValueExpressionGet(context, "#{miBean.propiedad}",String.class);

Si lo que queremos es acceder al bean para cambiar alguna propiedad, o lo que sea, podemos hacer lo siguiente:

ELContext contextoEL = context.getELContext( ); Application apli

= context.getApplication( );

ExpressionFactory ef = apli.getExpressionFactory( ); ValueExpression ve =.ef.createValueExpression(contextoEL, "#{ miBean }",MiClaseBean.class); MiClaseBean bean = (MiClaseBean) ve.getValue(contextoEL);

Una vez que tenemos la referencia al objeto, lo podremos manipular como queramos. También se puede acceder al contexto de aplicación este modo:

Application apli = FacesContext.getCurrentInstance().getApplication();

Aquí terminamos con este artículo. Si te ha gustado puedes dejar un comentario. En el próximo artículo veremos las distintas formas de definir flujos de navegación

Introducción a JSF Flujos de navegación JSF en Eclipse

Flujos de navegación JSF en Eclipse En la aplicación de ejemplo que mostramos en la introducción a Jsf 2.0, ya vimos la navegación implícita. En este artículo hablaremos sobre los distintos mecanismos para movernos entre las distintas páginas de nuestra aplicación.

Navegación implícita En el ejemplo comentado anteriormente, se mostraba un botón que contenía una acción:

Al pulsar dicho botón, si existe una página “confirmar” con la misma extensión que la página actual en la que estamos, se nos mostrará dicha página. Hay cuatro componentes que nos permiten realizar la navegación, estos son: o

h:commandButton y h:commandLink → Con el atributo “action”

o

h:button y h:link → Con el atributo “outcome”

La principal diferencia entre los componentes “commandButton“ y “button“ consiste en que el primero, realiza un HTTP POST, mientras que el segundo, realiza un HTTP GET, donde, si por ejemplo tenemos un formulario, el valor de los campos no sería enviado. Lo mismo ocurre en el caso de los enlaces. El valor de los atributos puede ser un literal, una expresión EL que devuelva un String, o un objeto Java con su método toString().

Navegación basada en reglas Se pueden definir flujos de navegación basados en reglas editando el fichero “facesconfig.xml”, analicemos el siguiente ejemplo:

pagina_de_origen.xhtml

#{ManagedBean.actionMethod} Condicion 1 /pagina_destino_1.xhtml

#{ManagedBean.actionMethod} Condicion 2 /pagina_destino_2.xhtml

Como se puede ver en el código anterior, se define una regla de navegación “navigation-rule” que tiene su origen en una página llamada “pagina_de_origen.xhtml”, desde esa página se inicia la navegación. Las reglas de navegación, tienen un origen y n casos de navegación “navigation-case“ que contienen los siguientes elementos: o

from-action → Un método de acción que devuelve un String, que se compara con la cadena definida en “from-otucome”, si los valores son iguales, nos moveríamos a la vista “to-view-id”.

o

from-outcome → Este valor se compara con “form-action” si se ha definido, o con el atributo “action” del “commandButton” o del “commandLink” que lance el evento.

o

to-view-id → La página de destino

Navegación basada en reglas en Eclipse Vamos a realizar un ejemplo sencillo que consistirá en un formulario de login que nos enviará a una página de bienvenida, si el usuario o el password es correcto, o a la página de error en caso contrario. Además se añade un enlace que nos llevará a una página de despedida. La aplicación tendrá esta pinta:

Como se ha comentado podemos centralizar la información de la navegación en un único archivo “faces-config.xml”. Esto nos permite utilizar herramientas para definir los flujos de manera visual. Por ejemplo, si abrimos el archivo anterior con la herramienta “Faces config editor” de Eclipse, se nos mostrará una ventana como la siguiente:

Como se puede ver, en la parte inferior, se muestran una serie de pestañas (Introduction, Overview… Etc.), que nos permiten definir nuestros flujos de manera visual. Vamos a ver un ejemplo de esto. Partiremos del proyecto de ejemplo que creamos en este post. En primer lugar, vamos a crear tres archivos: “bienvenido.xhtml”, “error.xhtml” y “hastapronto.xhtml”, para ello podemos copiar y pegar en la misma carpeta “webapp” el archivo “login.xml” y cambiarle el nombre. Vamos a editar todos estos archivos. La primera vista, “login.xhtml” quedará así:



Una aplicación simple JavaServer Faces

Introduzca usuario y contraseña













Si analizamos el código anterior, vemos que se utiliza un “h:panelGrid” para colocar los componentes. Hemos definido un botón que invoca a la función “validaUsuario” del managedbean, lo que nos permite definir un flujo dinámico en función del valor que devuelva esta función. Por otro lado, y a modo de ejemplo, hemos colocado un enlace para realizar una navegación estática, de modo que cuando se pinche en el enlace, nos muestre la página “hastapronto.xhtml”. La vista “bienvenido.xhtml” quedará de este modo:



Una aplicación simple JavaServer Faces



Las páginas “error.xhtml” y “hastapronto.xhtml” serán sililares, y mostrarán simplemente un mensaje, creo que no merece la pena poner el código.

Crear la regla de navegación Desde el “Faces Configuration Editor” de Eclipse que mostramos anteriormente, seleccionaremos la pestaña “Navigation Rule”. En la parte derecha, tenemos una paleta que podemos mostrar u ocultar. Dentro de la paleta, pinchamos una vez en “Page” y a continuación pinchamos en el área de trabajo de la derecha donde queramos que se represente la vista. Se nos abrirá un cuadro de dialogo donde podremos elegir el archivo “login.xhtml”. Hacemos lo mismo con el resto de vistas. Seguidamente, en la paleta pinchamos en “Link” y enlazamos “login.xhtml” con “bienvenido.xhtml”. A continuación, seleccionamos la flecha que se ha pintado y en la vista de propiedades, en el apartado “From Action” introducimos el método del managed-bean que queremos invocar:

“#{loginBean.validaUsuario}”. En el

campo “Form

Outcome”,

introducimos “usuarioCorrecto”. Repetimos lo anterior para el caso de la página “error.xhtml” y “hastapronto.xhtml” y guardamos los cambios con “Ctrl+S”. El resultado será similar a este:

Si pulsamos en la pestaña “Source” para ver el código fuente, vemos que se ha creado una regla de navegación por cada uno de los enlaces. Podemos dejar un único “navigation-rule”, de este modo:

login.xhtml /login.xhtml

#{loginBean.validaUsuario} usuarioCorrecto /bienvenido.xhtml

#{loginBean.validaUsuario} usuarioIncorrecto /error.xhtml

salir /hastapronto.xhtml

from-view-id Vamos a prestar atención al nodo, from-view-id, definida en la regla de navegación. En lugar de indicar una página de origen en particular, es posible utilizar comodines para hacer referencia a un grupo de páginas, a páginas dentro de un directorio, a todas las páginas, etc. Por ejemplo, si queremos colocar un enlace en todas las páginas de nuestra aplicación, que nos envíe a la página de login, podríamos configurarlo del siguiente modo: *

navigation-case Siguiendo con el análisis del código anterior, podemos ver que existen una serie de casos de navegación “navigation-case”. Todos estos casos se evalúan por orden de aparición, de modo que “el primero gana”, esto es, el primero que casa sus propiedades retorna su “to-view-id” para la navegación. Se pueden usar condiciones “if”, de modo que no es suficiente con que case el evento, sino que además debe cumplir la condición para que se envíe a su vista. Veamos un ejemplo de esto:

submit #{managedBean.booleano} /hastapronto.xhtml

Por otra parte, otra de las opciones que podemos utilizar es la etiqueta “redirect“, lo que provoca que el navegador del cliente realice una nueva petición HTTP, en lugar de presentar la respuesta sin realizar esa solicitud. Si en el ejemplo que estamos implementando en este artículo, pulsamos el enlace para salir de la aplicación, comprobaremos que la url de nuestro navegador sigue apuntando a login.xhtml, si queremos que cambie, tendremos que añadir “redirect“en la configuración del caso de navegación. También se pueden incluir en el “navigation-case” parámetros que estarán presentes en la redirección, del siguiente modo:

miParametro #{bean.largo * 2}

NOTA: Se puede indicar en la navegación implícita que realice “redirect”, añadiendo el parámetro “faces-redirect”. Por ejemplo:

Crear el Managed Bean Para crear el bean manejado, pulsaremos en la pestaña “ManagedBean” y a continuación pulsaremos el botón “Add”. Se nos abrirá una ventana de diálogo, donde podremos seleccionar una clase existente o bien crearla. Seleccionaremos esta última opción y pulsaremos siguiente:

Seleccionamos el paquete “com.notodocodigo” de nuestro proyecto de ejemplo y llamamos a nuestra clase “LoginBean” y pulsamos en siguiente. Dejamos los valores por defecto en la pantalla que se nos muestra y pulsamos en finalizar. Volvemos a guardar con “Ctrl+S”, si pulsamos de nuevo en la pestaña “Source” comprobamos que se ha creado la configuración del bean manejado.

loginBean com.notodocodigo.LoginBean session

Editamos el managed-bean y lo dejamos del siguiente modo:

package com.notodocodigo;

public class LoginBean {

private String usuario; private String password;

public LoginBean() { }

public String validaUsuario() { if (usuario.equals("Notodocodigo") && "pass".equals(password) ) { return "usuarioCorrecto"; } else { return "usuarioIncorrecto"; } }

public String getUsuario() { return usuario; }

public void setUsuario(String usuario) { this.usuario = usuario; }

public String getPassword() { return password; }

public void setPassword(String password) { this.password = password; } }

Como se puede ver en el método “validaUsuario()”, si se introduce el usuario “Notodocodigo” y el password “pass” se devuelve la cadena de texto utilizada en la regla de navegación para mostrar la página de bienvenida. El resultado de ejecutar el código es el siguiente:

Tratamiento de excepciones en los flujos de navegación Para tratar las excepciones que se puedan dar, de manera que se envíen a una determinada página en el caso de que se produzcan, hay que definir lo siguiente en el web.xml:

javax.faces.application.ViewExpiredException /faces/sessionExpired.xhtml



com.jsfcompref.BadUserException /faces/badUser.xhtml

Aquí termina este artículo. Puedes descargar el proyecto de ejemplo desde aquí. Si os ha gustado o tenéis alguna duda, podéis dejar un comentario. En el próximo artículo hablaremos sobre los componentes, validaciones y conversiones de datos en Jsf

Introducción a JSF Componentes, conversión y validación

Componentes, conversión y validación Componentes

Hay dos tipos de componentes de interfaz de usuario (UI Components), los que inician una acción, como los botones, por ejemplo, y los que proporcionan datos, como los campos de texto. El comportamiento de los componentes UI viene definido por los distintos interfaces de comportamiento que provee la especificación de Faces. Por ejemplo, un componente de acción implementará “UICommand”… y así sucesivamente. Casi todos los componentes UI extienden de la clase “UIComponentBase”, que implementa mucha de la funcionalidad necesaria en la construcción de componentes. Vamos a comentar algunos interfaces: o

EditableValueHolder → Tiene un “value” que puede ser editado por el usuario

o

ActionSource2 → Produce un “ActionEven” cuando es pulsado por el usuario

o

PartialStateHolder → Mantiene el estado desde un request hasta otro request

o

ValueHolder → Tiene un “value” que no puede ser editado por el usuario

o

ClientBehaviorHolder → Añade comportamiento al cliente, como por ejemplo de tipo Ajax NamingContainer → Los identificadores de los componentes hijo, deben ser únicos.

o

El core de los componentes UI se encuentra en el paquete “javax.faces.component”, como UIComponent, UIComponentBase, UIInput, etc.. Hay otro grupo de componentes que facilita el

desarrollo de clientes HTML,

y que

se encuentran en el

paquete

“javax.faces.component.html”. Normalmente extienden de los componentes del core, por ejemplo, HtmlCommandButton es un botón que se representa en html y extiende del core UICommand. La clase Renderer, habla con el UIComponent y es responsable de representar al componente en algún tipo de cliente, como un explorador web.

Accediendo a los componentes de manera programática En los anteriores ejemplos, hemos visto como se enlazaban las propiedades de un managedbean con los componentes Jsf. Si queremos tener acceso a los componentes propiamente dichos, de manera que podamos alterar su comportamiento, podemos incluirlos en el managedbean, y enlazarlos en la vista. Por ejemplo, vamos a crear una vista con un campo de texto de entrada, un campo de texto de salida y un botón. Queremos que cuando se pulse el botón, el texto de entrada se muestre en el de salida y además, que el campo de texto de entrada, se haga no editable y muestre un mensaje. Para ello definimos el managed-bean, del siguiente modo. @ManagedBean(name="beanAcceso") @RequestScoped public class AccesoComponentesBean {

private HtmlInputTextarea textoEntrada; private HtmlOutputText textoSalida;

public AccesoComponentesBean() { }

public String mostrarEntradaEnSalida() { textoSalida.setValue(textoEntrada.getValue()); textoEntrada.setValue("El texto se ha incluido"); textoEntrada.setReadonly(true);

return "inicio"; }

//Getters y setters aquí... }

Y creamos la página “inicio.xhtml”, con este contenido:



Ejemplo de acceso a componentes









Como se puede ver, con “binding”, enlazamos el componente. De este modo, si ejecutamos el ejemplo, y escribimos algo en la caja de texto, al pulsar el botón se mostrará algo como esto:

Podemos comprobar que ahora el textarea es de solo lectura. En general es recomendable enlazar propiedades en lugar de enlazar componentes.

Conversión y validación de datos Como ya hemos visto, empleando validaciones, nos aseguramos de que los datos tienen el valor correcto y con las conversiones, nos aseguramos de que es del tipo adecuado. Las validaciones se aplican a componentes de entrada, sin embargo las conversiones se pueden aplicar a componentes de entrada o de salida. Por ejemplo: Iva aplicable:



Se mostraría, por ejemplo, como: “Iva aplicable: 22%”. En el siguiente ejemplo se aplican a la vez validaciones y conversiones personalizadas.





Los

conversores

y

los

de javax.faces.convert.Converter,

validadores,

normalmente

son

instancias

y javax.faces.validator.Validator respectivamente,

y

producen una salida “success” o “failure” que altera el flujo de la aplicación.

Conversión El interfaz javax.faces.convert.Converter define estos dos métodos:

public Object getAsObject(FacesContext context, UIComponent component, String value); public String getAsString(FacesContext context, UIComponent component, Object value);

El parámetro context, hace referencia al “facesContext” del request, component, es el componente del que se toma el valor que se va a convertir (es aconsejable, que en la implementación del conversor, no se altere el estado del componente) y “value” es el valor a convertir. Cuando se representa el componente en la vista, se invoca a getAsString(). En el proceso de envío de datos, se invoca a getAsObjet(), para convertir la cadena al tipo de dato adecuado.

Cuando editamos un componente en una vista, es sencillo conocer desde eclipse los conversores estándar, pulsando “Ctrl+espacio”. Por ejemplo:

DateTimeConverter y NumberConverter tienen

sus

propias

etiquetas “$lt;f:convertdatetime$gt;” y “$lt;f:convertnumber$gt;”, y que permiten añadir configuración,

como

por

ejemplo

el

patrón

a

utilizar:

“$lt;f:convertdatetime

pattern=”dd/MM/yyyy”$gt;”. Esto sería lo que se llama, definir la conversión de manera explícita. En la etiqueta “$lt;f:converter$gt;”, en el parámetro converterId se especifica un conversor específico definido en el archivo faces-config.xml, o anotado con @FacesConverter, o por la clase. Un ejemplo de definición de un conversor en el archivo faces-config.xml, podría ser el siguiente:

Conversor de cantidades, según el locale y las preferencias del usuario

conversorDePeso

com.notodocodigo.conversores.ConversorCantidades

Como hemos comentado anteriormente, esta clase “ConversorCantidades” implementaría “javax.faces.convert.Converter”. También se puede registrar un conversor, por el tipo de objeto que puede convertir, por ejemplo, si implementamos la clase, y la anotamos de este modo:

@FacesConverter(forClass="java.lang.Float")

O bien, la definimos en faces-config.xml de este modo:

Conversor de cantidades, según el locale y las preferencias del usuario

java.lang.Float

com.notodocodigo.conversores.ConversorFloat

Cuando se asocia un componente con una propiedad del bean, de un determinado tipo, se está realizando una conversión implícita. Los tipos pueden ser: o

java.math.BigDecimal

o

java.math.BigInteger

o

java.lang.Boolean

o

java.lang.Byte

o

java.lang.Character

o

java.lang.Double

o

java.lang.Float

o

java.lang.Integer

o

java.lang.Long

o

java.lang.Short

Por ejemplo, en:

Como el campo “peso”, del bean ”producto” es de tipo “Integer” (por ejemplo), automáticamente se asocia el conversor “javax.faces.convert.IntegerConverter” de manera implícita.

Validación El interfaz “javax.faces.validator.Validator” define un método “valídate()”: public void validate(FacesContext context, UIComponent component, Object value):

El parámetro context, hace referencia a “facesContext” del request, “component”, es el componente del que se toma el valor que se va a validar, y “value” es el valor a validar. El método debe lanzar una excepción ValidatorException, en el caso de que falle la validación. Existen validaciones estándar, que como en el caso anterior, se puede mostrar en Eclipse editando la vista y pulsando “Ctrl+espacio”:

Vamos a ver algunos ejemplos:









Se pueden añadir componentes entre un par de etiquetas “f:validaterequired” de modo que en lugar de poner componente por componente el flag requierd=”true”, se aplique esta condición a un grupo de componentes. Las validaciones disponen de una propiedad disabled=”” de modo que si está a true, no se aplica la validación. Se pueden utilizar expresiones EL para configurar el valor de esta propiedad.

Implementar Validadores Se puede utilizar un método de un bean como validador, siempre y cuando, dicho método tenga los mismos parámetros y devuelva lo mismo que Validator.validate( ). Por ejemplo:

El método, se definirá como:

public void validaValor (FacesContext context, UIComponent component, Object value) {

//El código de validaciób va aquí

}

Este tipo de asociación, únicamente permite un validador. Otro modo de crear un validador, es implementar el interfaz “Validator”. La clase se registrará con un validator-id, para lo cual, se utiliza la siguiente anotación:

@FacesValidator(value="miValidador") public class MiValidadorPersonalizado implements Validator {

public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {

//El código de validación va aquí

} }

En lugar de la anotación @FacesValidator, se puede incluir en el fichero faces-config.xml

Validador personalizado bla, bla, bla...

miValidador

com.notodocodigo.validadores.MiValidadorPersonalizado

Aquí terminamos este artículo. Hemos hablado de conversores y validadores y hemos visto que Jsf ofrece la posibilidad de usar las implementaciones estándar de estos componentes. Cuando falle la conversión o la validación se mostrará el mensaje correspondiente, pero ¿Qué ocurre si

queremos personalizar dicho mensaje?. Hablaremos de esto en el próximo artículo: Internacionalización

Introducción a JSF Internacionalización

Internacionalización Jsf tiene soporte para internalización, también llamado i18n, donde 18 es el número de letras entre la i y la n en “internationalization”. Para saber el idioma que vamos a utilizar se utiliza el “Locale”, que representa una combinación del código del idioma y del código del país. El código del idioma es obligatorio, y está formado por dos letras en minúscula, por ejemplo “en”, para el Inglés o “es” para el Español. El código del país, se representa con dos letras mayúsculas, por ejemplo “ES”, para España. Locales válidos pueden estar formados únicamente por el idioma, como “en”, o “es”, o bien, por la combinación del idioma y el país, como por ejemplo “en_US” o “es_ES”.

Definir los locales soportados Para configurar el locale, hay que especificar los locales que se van a utilizar en el fichero faces-config.xml

es en es_ES en_US

MensajesUtilizados

Estamos indicando que el idioma por defecto es el español, y que se soportan otros locales como el inglés, el español de España y el inglés de Estados Unidos. Una vez que hemos especificado los idiomas a utilizar, tenemos que crear los archivos de recursos con las distintas traducciones “resource bundles”. Estos ficheros están formados por pares, clave/valor, con las claves a utilizar y los mensajes. Las claves son las mismas para todos los locales, por

ejemplo, podríamos crear

un fichero para

el

idioma español

“literales_es.properties”, con este contenido:

saludo=Hola mundo! errorFueraDeRango =Error! Fuera de rango

Y su equivalente para el idioma inglés en el fichero “literales_en.properties”:

saludo=Hello world! errorFueraDeRango=Error! Out of range

NOTA: Dos cosas a tener en cuenta. 1. No se pueden utilizar puntos “.”, en las claves, ya que es un valor reservado en expresiones EL. 2. Una vez creado el fichero de recursos habrá que codificarlo usando caracteres Unicode.

Como se puede ver, el nombre de los ficheros está formado por “literales” seguido del idioma y de “.properties”. El fichero de recursos a utilizar por defecto se llamaría, en este ejemplo “literales.properties”. Los ficheros de recursos se colocarán en el classpath

Personalizar los mensajes Jsf En el caso de los validadores y conversores estándar, se utilizan mensajes predefinidos, podemos ver algunos de ellos:

javax.faces.component.UIInput.REQUIRED = Validation Error: Value is required javax.faces.component.UISelectOne.INVALID = Validation Error: Value is not valid. …

Podemos personalizar estos mensajes para los distintos idiomas que nos interese, sobreescribiendo estas claves en el fichero de recursos multiidioma, de modo que se muestre el texto que nos interese. Por ejemplo, podríamos añadir las siguientes claves, en el fichero “literales_es_ES.properties”:

javax.faces.component.UIInput.REQUIRED=Se requiere este dato. javax.faces.component.UIInput.REQUIRED_detail=Por favor, complete este dato.

En “basename” se especifica el nombre del fichero en el classpath, en el ejemplo anterior, hemos colocado los ficheros multiidioma en el paquete “com.notodocodigo.i18n”. Con “var”, le damos una etiqueta para tener acceso a cada clave multiidioma. Ahora podremos acceder a la clave “saludo”, con la expresión “#{msg.saludo}”. También se puede especificar el fichero de recursos de manera global en faces-config.xml, de este modo:



com.notodocodigo.i18n.literales msg

Determinar el locale actual a utilizar El locale a utilizar se determina a partir del locale configurado en la aplicación cliente. Los navegadores web, envían una cabecera HTTP, que especifica el lenguaje soportado. En Jsf el locale a utilizar se determina por la unión del locale del navegador y los locales soportados, aunque se puede determinar un locale configurándolo en la vista con la etiqueta . Aquí finalizamos este artículo, en el próximo hablaremos sobre los eventos en Jsf. Si te ha gustado el artículo, no dudes en poner un comentario.

Eventos Jsf proporciona un modelo de programación basado en eventos, similar a otros utilizados en clientes pesados como Swing. Se basa en la utilización de eventos y escuchadores (event/listener), de modo que un componente lanzará un evento, como por ejemplo cuando escribimos en un campo de texto y otro elemento escuchará al componente para realizar una determinada acción. Anteriormente hablamos sobre dos tipos de componentes, los que inician una acción, como un botón, y los que contienen información, como un campo de texto. También vimos que el primer grupo de componentes implementaba “ActionSource2”, este interfaz utiliza un “action event” y su correspondiente “action listener” para disparar y procesar los eventos. El segundo grupo

de componentes implementa “ValueHolder” o “EditableValueHolder” y utilizan “value change event” y “value change listener” para realizar alguna acción cuando el contenido cambia. Estos serían los eventos de aplicación. Además de manejar los eventos en lo relativo a los componentes UI, Jsf proporciona eventos de fase (phase events) y escuchadores de fase (phase listeners) que se utilizan en el propio ciclo de vida de procesamiento de peticiones. Esto permite registrar y procesar acciones cuando se producen cambios de estado en el ciclo de vida de una petición. Los eventos de fase, se producen antes y después de cada fase del ciclo de vida de una petición Jsf. En otra categoría estarían los eventos de sistema (system events), que son como los eventos de fase pero con una granularidad mayor, como por ejemplo “la aplicación se va a iniciar”. Una subcategoría de esto, serían los eventos de sistema que ocurren a nivel de componente, algo así como: “se va a validar este componente”.

Eventos de aplicación Action Event Anteriormente, cuando vimos la primera aplicación de ejemplo en la introducción a Jsf ya vimos como al pulsar el botón: Donde el método de acción sería el siguiente: public String enviarMensaje() { FacesMessage mensajeResultado = new FacesMessage("El mensaje ha sido enviado"); String salida = "resultado"; //Si queremos ir al inicio (mensaje.xhtml) hay que poner salida="mensaje" FacesContext.getCurrentInstance().addMessage(null, mensajeResultado); return salida; }

Hay dos tipos de métodos de acción, “action method” o “action listener method”. El primero, es un método java que no recibe parámetros y devuelve un String que se recoge por el “NavigationHandler” para determinar el flujo de navegación, como en el ejemplo anterior.

El segundo “action listener method”, no devuelve nada y recibe un “ActionEvent”, por ejemplo: NOHAY NAA

Donde el método, debe tener la siguiente firma: public void escribirLog(ActionEvent ae) { //Implementación de ejemplo System.out.println("Mira cómo escribo!!"); }

Estos dos métodos se comportan básicamente del mismo modo, los datos del formulario, son enviados, validados, y si son correctos, se ejecutan las acciones. Si lo que queremos es que se ejecuten las acciones sin que entre en la siguiente fase del ciclo de vida de la aplicación, de modo que no se validen los campos, etc. hay que añadir immediate=”true”, de este modo: NO HAY NAA

Hay que tener en cuenta, que si queremos leer valores de otros campos, como por ejemplo campos de texto, etc. no podemos garantizar que estamos recuperando el valor actual que contienen estos campos, a no ser que cada uno de ellos también tenga el atributo immediate=”true”.

Value Change Event Value change event es el evento que se usa para indicar que el contenido de un component ha cambiado. Normalmente se trata de componentes que implementan “ValueHolder” or “EditableValueHolder” como por ejemplo los campos de texto. El uso típico que se da a este tipo de evento, puede ser, actualizar una lista de provincias, cuando se selecciona un país, o actualizar una serie de listas desplegables en función del valor seleccionado en un campo. El modo de aprovechar este tipo de eventos, es similar al comentado anteriormente, por ejemplo:

NO HAY NAA

Donde el método, tendría esta firma:

public void cambioDeValor(ValueChangeEvent vce) { System.out.println("Ha cambiado el valor del campo!"); }

Como se puede ver, hemos configurado immediate=”true”, para que se invoque al método cada vez que cambie el valor.

Action y value change listeners personalizados Podemos escribir nuestras propias clases action listener o value change listener personalizadas, implementando

respectivamente ActionListener o ValueChangeListener.

ejemplo de cada uno de ellos:

public class EscuchadorDeAccion implements ActionListener {

public EscuchadorDeAccion () { }

public void processAction(ActionEvent ae) { System.out.println("Procesando un evento de acción!"); } }

public class EscuchadorDeCambioDeValor implements ValueChangeListener {

Veamos

un

public EscuchadorDeCambioDeValor () { }

public void processValueChange(ValueChangeEvent vce) { System.out.println("Procesando un evento de cambio de valor!"); } }

Para usar estas clases, hay que anidar respectivamente una etiqueta f:actionlistener y una etiqueta f:valueChangelistener en el componente que corresponda, del siguiente modo:

NO HAY NAA

En el campo de texto, se ha incluido una función javascript, de modo que cuando cambie el foco del componente, por ejemplo, pulsando el tabulador, se haga submit del formulario.

Todo junto Vamos a ver un ejemplo completo. Definiremos un formulario con un campo de texto que reciba el código de producto, de modo que cuando cambie el foco, si el código empieza por “xxx”, se rellenen los campos “Producto” y “Modelo”. Además se añadirá un campo “Más” que en el caso de estar marcado, mostrará un área de texto. Para ello, definimos la siguiente vista “inicio.xhtml”:

Producto Como se puede ver usamos immediate=”true”, en todos los campos, para saltarnos las validaciones, si las hubiera. Se envían los datos a través de un botón oculto utilizando la llamada javascript onchange=”document.getElementById(„cargar‟).click();”. Para mostrar y

ocultar campos, se utiliza rendered=”#{bean.mostrarMasInfo}” donde “mostrarMasInfo” es un valor booleano. El managed-bean queda del siguiente modo:

@ManagedBean(name = "bean") @RequestScoped public class BeanCarga {

private HtmlInputText codigo; private HtmlInputText producto; private HtmlInputText modelo;

private HtmlInputTextarea masInfo; private HtmlSelectBooleanCheckbox masInfoCheck; boolean mostrarMasInfo = false;

public void rellenarListener(ValueChangeEvent changeEvent) {

String valor = changeEvent.getNewValue().toString();

//Implementación de prueba, solo para Madrid y Toledo if ( valor != null && valor.startsWith("xxx")){ producto.setValue("Fiat"); modelo.setValue("Bravo"); } else { producto.setValue("Desconocido");

modelo.setValue("Desconocido"); } }

public void mostrarInfo(ValueChangeEvent changeEvent) { setMostrarMasInfo((Boolean) changeEvent.getNewValue()); }

//Getters y setters...

Al ejecutar la aplicación se mostrará algo parecido a lo siguiente:

Cuando introducimos “xxx” y pulsamos el tabulador, o pinchamos en otros campos, se nos cargan los campos “Producto” y “Modelo”. Si marcamos “Más”, se nos muestra el área de texto “Detalles”.

Quizá esta no sea la manera más interesante de implementar este tipo de comportamiento. Lo más adecuado es utilizar Ajax, de modo que no haya que enviar la página completa para que sea reenviada al cliente con los campos rellenos.

PhaseListener La mejor manera de ver como se ejecuta es implementar un escuchador de fase que se ejecute en todas las fases y saque por consola lo que está haciendo. Declaramos la clase en el facesconfig.xml

mis.paquetes.MiListener

Mi listener, implementará “PhaseListener”, los métodos a implementar reciben un objeto PhaseEvent, que podemos volcar en una consola con phaseEvent.getPhaseId().toString(). Al ejecutar la aplicación podremos observar como se va pasando por cada fase del ciclo de vida de una petición Jsf.

Simple CRUD Using JSF (standard) and MySQL with netbeans and Tomcat The Data 

The database (an example)

create database TestDB; use TestDB;

CREATE TABLE TestDB.`users` ( `userid` int(11) NOT NULL AUTO_INCREMENT, `firstname` varchar(45) DEFAULT NULL, `lastname` varchar(45) DEFAULT NULL, `dob` date DEFAULT NULL, `email` varchar(100) DEFAULT NULL, PRIMARY KEY (`userid`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 The handy ORM



The entity (POJO)

public class User { private int userid; private String firstName; private String lastName; private Date dob; private String email; public int getUserid() { return userid; } public void setUserid(int userid) { this.userid = userid; } public String getFirstName() { return firstName;

} public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Date getDob() { return dob; } public void setDob(Date dob) { this.dob = dob; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "User [userid=" + userid + ", firstName=" + firstName + ", lastName=" + lastName + ", dob=" + dob + ", email=" + email + "]"; } } 

The DAO (insert, select, update, delete)

1 2 package dao; 3 4 import entities.User; 5 import gm.GM; 6 import java.sql.Connection; 7 import java.sql.PreparedStatement; 8 import java.sql.ResultSet;

9 import java.sql.SQLException; 10 import java.sql.Statement; 11 import java.util.ArrayList; 12 import java.util.List; 13 import javax.naming.NamingException; 14 15 /** 16 * 17 * @author pascalfares 18 */ 19 public class UserDao { 20 public static void addUser(User user) throws SQLException, NamingException { 21 Connection connection = GM.getTestDB().getConnection(); 22 PreparedStatement preparedStatement = connection 23 .prepareStatement("insert into users(firstname,lastname,dob,email) values (?, ?, ?, ? )"); 24 // Parameters start with 1 25 preparedStatement.setString(1, user.getFirstName()); 26 preparedStatement.setString(2, user.getLastName()); 27 preparedStatement.setDate(3, new java.sql.Date(user.getDob().getTime())); 28 preparedStatement.setString(4, user.getEmail()); 29 preparedStatement.executeUpdate(); 30 31 32 } 33 34 public static void deleteUser(int userId) throws NamingException, SQLException { 35 Connection connection = GM.getTestDB().getConnection(); 36 PreparedStatement preparedStatement = connection 37 .prepareStatement("delete from users where userid=?"); 38 // Parameters start with 1 39 preparedStatement.setInt(1, userId); 40 preparedStatement.executeUpdate(); 41 42 } 43 44 public static void updateUser(User user) throws NamingException, SQLException {

45 Connection connection = GM.getTestDB().getConnection(); 46 PreparedStatement preparedStatement = connection 47 .prepareStatement("update users set firstname=?, lastname=?, dob=?, email=?" + 48 "where userid=?"); 49 // Parameters start with 1 50 preparedStatement.setString(1, user.getFirstName()); 51 preparedStatement.setString(2, user.getLastName()); 52 preparedStatement.setDate(3, new java.sql.Date(user.getDob().getTime())); 53 preparedStatement.setString(4, user.getEmail()); 54 preparedStatement.setInt(5, user.getUserid()); 55 preparedStatement.executeUpdate(); 56 57 58 } 59 60 public static List getAllUsers() throws NamingException, SQLException { 61 List users = new ArrayList(); 62 Connection connection = GM.getTestDB().getConnection(); 63 Statement statement = connection.createStatement(); 64 ResultSet rs = statement.executeQuery("select * from users"); 65 while (rs.next()) { 66 User user = new User(); 67 user.setUserid(rs.getInt("userid")); 68 user.setFirstName(rs.getString("firstname")); 69 user.setLastName(rs.getString("lastname")); 70 user.setDob(new java.util.Date(rs.getDate("dob").getTime())); 71 user.setEmail(rs.getString("email")); 72 users.add(user); 73 } 74 75 76 return users; 77 } 78 79 public static User getUserById(int userId) throws NamingException, SQLException { 80 User user = new User(); 81 Connection connection = GM.getTestDB().getConnection();

82 PreparedStatement preparedStatement = connection. 83 prepareStatement("select * from users where userid=?"); 84 preparedStatement.setInt(1, userId); 85 ResultSet rs = preparedStatement.executeQuery(); 86 87 if (rs.next()) { 88 user.setUserid(rs.getInt("userid")); 89 user.setFirstName(rs.getString("firstname")); 90 user.setLastName(rs.getString("lastname")); 91 user.setDob(rs.getDate("dob")); 92 user.setEmail(rs.getString("email")); 93 } 94 95 return user; 96 } 97 } 98 The UI

 The ManagedBean Create the UserControl managedBean

1 package mb; 2 3 import dao.UserDao; 4 import entities.User; 5 import java.sql.SQLException; 6 import java.util.List; 7 import javax.faces.bean.ManagedBean; 8 import javax.faces.bean.SessionScoped; 9 import javax.naming.NamingException; 10 11 /** 12 * A minimal Managed Bean for CRUD 2 attributes 13 * one for current User 14 * one for the list of users 15 * @author pfares 16 */ 17 @ManagedBean 18 @SessionScoped 19 public class UserControl { 20 private User selectedUser; 21 private List lusers; 22 /** 23 * Creates a new instance of UserControl

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 }

*/ public UserControl() { selectedUser=new User(); } /** * @return the selectedUser */ public User getSelectedUser() { return selectedUser; } /** * @param selectedUser the selectedUser to set */ public void setSelectedUser(User selectedUser) { this.selectedUser = selectedUser; } /** * @return the lusers */ public List getLusers() throws NamingException, SQLException { lusers=UserDao.getAllUsers(); return lusers; } /** * @param lusers the lusers to set */ public void setLusers(List lusers) { this.lusers = lusers; } public String create() throws SQLException, NamingException { UserDao.addUser(selectedUser); return "index"; }

Minimal pages : List Users and Create Users

1 2 3 6 7 Facelet Title 8 9

10

11

12

13 #{item.firstName} 14 #{item.lastName} 15 #{item.userid} 16 #{item.email} 17

18

19

20

21

22

23 24 25 === 1 2 3 6 7 Crud Example : Create a user 8 9 10

11

12

13

14

15

16

17

21

23

24

25

26

27

28

29

30

31 32