y
Una tabla
h:panelGroup
Agrupa un conjunto de componentes como un
único hijo de un elemento padre
Un elemento HTML o
Una fila de una tabla
Un elemento HTML
Muestra un conjunto de check boxes para que el Un conjunto de elementos HTML
usuario seleccione múltiples valores
de tipo checkbox
Un conjunto de check boxes
h:selectManyListbox
Permite a un usuario elegir múltiples valores de Un elemento HTML
un conjunto de elementos que se muestran
como una lista
Una lista
h:selectManyMenu
Permite a un usuario elegir múltiples valores de Un elemento HTML
un conjunto de elementos que se muestran
como una menú desplegable
Un menú desplegable
h:selectOneListbox
Permite a un usuario elegir un valor de un
conjunto de elementos que se muestran como
una lista
Un elemento HTML
Una lista
h:selectOneMenu
Permite a un usuario elegir un valor de un
conjunto de elementos que se muestran como
una menú desplegable
Un elemento HTML
Un menú desplegable
h:selectOneRadio
Permite a un usuario elegir un valor de un
conjunto de elementos que se muestran como
radio buttons
Un conjunto de elementos HTML
Un conjunto de opciones
- h:inputText:
En este código estamos dibujando un campo de texto de rellenado obligatorio con un tamaño de 20
caracteres. Además referenciamos un mensaje de error en un fichero de recursos en caso de que no
se introduzca ningún texto, y asociamos un oyente de cambio de valor usando la etiqueta core
f:valueChangeListener. Como veremos más adelante, el oyente puede ser o bien un método dentro
de un bean o, como en este caso, una clase que implemente el interfaz ValueChangeListener.
- h:inputSecret:
Enlazamos el texto que introduzca el usuario con la propiedad password del bean loginBean. El
atributo redisplay indica si se vuelve a mostrar el contenido del campo en caso de redibujar la
página (normalmente debido a errores de validación de algún componente).
- h:outputLabel:
En este caso estamos asociando una etiqueta al check box representado por el elemento con id
fanClub. El valor de la etiqueta viene dado por la clave DukeFanClub dentro del fichero de recursos.
Es necesario hacer notar que, en este ejemplo, estamos utilizando el atributo binding en los
elementos selectBooleanCheckbox y outputLabel para asociar dichos componentes a objetos reales
declarados en el lado servidor; es decir, la propiedad cashierBean.specialOffer es del tipo
javax.faces.component.html.HtmlSelectBooleanCheckbox, y la propiedad specialOfferText del
bean cashierBean es un objeto de tipo javax.faces.component.html.HtmlOutputLabel. JSF se
encarga de asignar el componente real a la propiedad para permitirnos un mayor control del
componente, en caso de que sea necesario.
La etiqueta h:outputText es, en este caso concreto, innecesaria, ya que podríamos haber asignado el
valor de dicha etiqueta directamente al elemento h:outputLabel de esta forma:
- h:outputLink: La etiqueta h:outputLink se utiliza para dibujar un hiperenlace que enlaza con otra
página pero no genera un evento de acción. Si quisiéramos realizar algún procesamiento con la
pulsación de un hiperenlace utilizaríamos la etiqueta h:commandLink.
Documentation for this demo
- h:outputFormat: Esta etiqueta nos permite mostrar mensajes concatenados tal y como se describe
en la documentación de java.text.MessageFormat:
- h:commandButton y h:commandLink: Estas dos etiquetas, dibujadas como un botón y un
hiperenlace respectivamente, nos permiten realizar una acción y navegar a otra página al mismo
tiempo. Para ello utilizamos el atributo action, cuyo valor debe ser el alias de la página a la que
queremos navegar. También podemos asociar un actionListener que realice alguna acción sin
navegación asociada:
/h:commandLink>
En el primer caso el atributo action hace referencia al método submit del bean cashierBean, que
realizará un procesamiento determinado y devolverá un string que será tomado como indicativo de a
qué página debemos navegar; en el segundo, tenemos un atributo action que indica directamente la
página resultante, y un listener representado por un objeto de la clase LinkBookChangeListener,
objeto que debe implementar el interfaz javax.faces.event.ActionListener.
- h:graphicImage:
- h:panelGrid y h:panelGroup: Un panel en JSF se utiliza para agrupar elementos que son
dibujados como celdas de una tabla. h:panelGrid se utiliza para dibujar tablas reales HTML;
h:panelGroup agrupa otros elementos en una única celda o, utilizado fuera de un h:panelGrid,
agrupa elementos y texto plano (por ejemplo, con un atributo rendered común que muestra u oculta
dichos elementos, como vimos en el ejemplo inicial).
...
Este panelGrid define una tabla con dos columnas y n filas. Las filas son determinadas por el
número de componentes anidados en el panelGrid, el primer componente va a la primera celda, el
segundo a la segunda, el tercero a la primera celda de la segunda fila, etc. Si especificamos un
facelet con name header, o footer, o caption, se genera un elemento th expandido al número de
columnas de la tabla. Si existe un facelet header debe estar definido como primer elemento, si
existe un facelet footer debe estar definido como último elemento. Si necesitamos celdas
diferentes como header tendremos que recurrir a estilos (atributos style o styleClass) en los
elementos correspondientes.
Un elemento facelet sólo puede tener un elemento hijo por lo que si necesitamos anidar más de un
elemento dentro de un facelet los agruparíamos dentro de un h:panelGroup. De la misma forma,
también utilizaríamos h:panelGroup para agrupar varios elementos JSF dentro de una misma celda.
También vemos en este ejemplo la referencia a diferentes listeners, conversores y validadores,
además de la utilización de la etiqueta h:message asociada al elemento con id ccno como un
elemento más del panelGrid.
Otro ejemplo más sencillo:
Enter a number :
Aquí definimos un panelGrid con tres columnas (y, por el número de elementos, una única fila); la
primera celda contendrá el texto Enter a number; la segunda, un campo de texto, y la tercera, el
posible mensaje de error.
- h:selectBooleanCheckbox: Utilizamos esta etiqueta para mostrar un checkbox.
Este ejemplo muestra la utilización del atributo binding; en este caso lo utilizamos, además de para
acceder al valor del elemento checkbox (para lo cuál podríamos haber utilizado simplemente una
propiedad del bean y asociarla al elemento mediante el atributo value), para poder acceder a los
componentes asociados a las etiquetas en el lado servidor, y modificar el atributo rendered de forma
programática.
- h:selectOneRadio, h:selectOneMenu y h:selectOneListbox: Estas etiquetas muestran un
conjunto de radio buttons, un menú desplegable o una lista de elementos, respectivamente, de los
cuáles sólo podemos seleccionar un elemento. La utilización de estas tres etiquetas varía muy
ligeramente, por ejemplo, h:selectOneListBox es la única que define un atributo size para
especificar el número de elementos que se muestran de la lista.
El atributo value de selectOne... contendrá el valor seleccionado de entre todos los elementos
(también podríamos usar un atributo binding y asociar un objeto que representa al componente).
Cada valor posible a seleccionar lo especificamos con una etiqueta f:selectItem.
Muchas veces necesitamos especificar el conjunto de elementos posible de forma dinámica. Para
ello utilizamos, no etiquetas f:selectItem, sino una única etiqueta f:selectItems:
El atributo value de la etiqueta f:selectItems hace referencia a una propiedad de un bean que
contiene los elementos a mostrar en la lista. Esta propiedad puede ser de tipo Array, Map o
Collection:
•
•
•
•
En caso de ser un Map, la clave de cada objeto indica el valor asociado al elemento
correspondiente de la lista, y el objeto se usa para imprimir el label asociado a dicho
elemento.
En cualquier otro caso, los elementos del array o colección pueden ser objetos SelectItem o
POJOs.
Un objeto SelectItem contiene propiedades value, label o disabled, que nos permiten
configurar cada elemento a dibujar en la lista.
En caso de utilizar POJOs, el framework, en su forma más simple, utiliza el mismo valor
para el value y label de cada elemento; o podemos tener un control más complejo del
resultado, de esta forma:
Aquí estamos utilizando dos etiquetas f:selectItem y f:selectItems diferentes para mostrar los
elementos. La etiqueta f:selectItem muestra un elemento no seleccionable (noSelectionOption =
"true") que provocará un error de validación en caso de que este elemento esté seleccionado al
enviar el formulario (para que este error de validación se produzca es necesario también que la
selección de un elemento sea obligatoria, como en este caso).
La etiqueta f:selectItems declara varios atributos. El atributo value enlaza con una propiedad array
o colección del bean bean. Esta colección contiene objetos Country con una serie de propiedades a
las que accedemos directamente gracias a la declaración del atributo var.
- h:selectManyCheckBox, h:selectManyMenu y h:selectManyListbox: Estas etiquetas funcionan
igual que las etiquetas h:selectOne... con la diferencia de que podemos seleccionar múltiples
valores, por lo que su atributo value debe apuntar a una List, un array de objetos o un array de tipos
primitivos.
- h:dataTable: Esta etiqueta nos permite generar tablas dinámicas.
En este ejemplo mostramos una tabla en la que cada fila corresponde a un elemento de la colección
gestorEmpleados.empleados. El atributo value de un dataTable referencia a los datos a mostrar en la
tabla, que pueden tomar las siguientes formas:
•
•
•
•
•
•
•
Una lista de beans
Un array de beans
Un único bean
Un objeto javax.faces.model.DataModel
Un objeto java.sql.ResultSet
Un objeto javax.servlet.jsp.jstl.sql.Result
Un objeto javax.sql.RowSet
Todos estos tipos son encapsulados en un DataModel. Si no especificamos un
implementación JSF proporciona uno que encapsula los datos subyacentes.
DataModel,
la
La etiqueta h:dataTable permite también mostrar un subconjunto de los datos disponibles mediante
los atributos first y rows. El atributo first especifica el primer elemento a mostrar. El atributo
rows especifica el número de filas a mostrar. Por defecto ambos atributos tienen un valor de 0.
- h:message y h:messages: Estas etiquetas nos permiten mostrar mensajes de error asociados a
validaciones o conversiones de componentes. h:message muestra un mensaje asociado a un
componente (si hay varios, el primero que encuentre), mientras que h:messages muestra todos los
mensajes asociados a un componente, todos los mensajes de todos los componentes y/o mensajes
globales no asociados a ningún componente.
En este ejemplo se van a mostrar todos los errores de validación y conversión asociados al
inputText con id userNo debajo del botón submit. Podemos ver cómo estamos registrando un
validador estándar para limitar el rango posible de valores a insertar entre userNumberBean.minimum
y userNumberBean.maximum.
La etiqueta h:messages declara, entre otros, los atributos globalOnly (por defecto igual a false) y
for. Si el atributo for es especificado, se muestran todos los mensajes asociados al componente
indicado. Si for no es especificado y globalOnly toma el valor de true se muestran únicamente los
mensajes globales (no asociados a ningún id de cliente). Si for no es especificado y globalOnly es
false, se muestran todos los mensajes asociados a la página.
¿Cómo especificamos el mensaje a mostrar?
Por defecto JavaServer Faces proporciona unos mensajes por defecto asociados a unos códigos.
Para empezar a personalizar este mensaje lo primero sería añadir el atributo label al componente
que estamos validando, de esta forma:
Esto muestra el mensaje estándar de validación long range pero sustituyendo en el mensaje el id del
componente por la expresión indicada en la label.
Si queremos sustituir el mensaje completamente una opción sería asignar el nuevo mensaje al
código correspondiente en el fichero de recursos (ver capítulo referente a i18n). Los códigos
estándar de mensajes se encuentran encuentran en la especificación de jsf, capítulo 2.5.2.4, o en el
fichero Messages.properties del paquete javax.faces del fichero jsf-api...jar. Por ejemplo el código
javax.faces.component.UIInput.REQUIRED indica el mensaje a mostrar en caso de que un
componente sea obligatorio y no se suministre valor alguno.
Otra posibilidad es especificar el mensaje a mostrar como atributo del componente correspondiente,
por ejemplo:
En este caso el valor del atributo requiredMessage sustituiría al mensaje por defecto en caso de que
no se proporcionara ningún valor al inputText.
Podemos hacer lo mismo para el resto de validadores estándar con el atributo
componente a validar:
validatorMessage
del
Eso sí, si quisiéramos especificar varios validadores para el mismo componente y utilizar mensajes
personalizados para cada validador, tendríamos que utilizar la técnica de sustitución de los mensajes
de error estándar en el fichero de recursos.
Finalmente, en caso de utilizar validadores personalizados podremos especificar de forma
programática el mensaje de error asociado a esa validación. Veremos esta técnica en el capítulo
dedicado a validadores.
Ejemplos de Uso de Etiquetas Html
En este capítulo vamos a ver un ejemplo de utilización de menús y radio buttons para ver más
claramente cómo utilizar las etiquetas
Rellenado de combos
Rellenado de combos
Cuando elijas una región en el combo de la izquierda, se rellenará la lista de la
derecha con sus provincias correspondientes
Has accedido a esta página #{sesBean.accesosAPagina}
#{exCmBean.mensajeFavorito}
Podemos diferenciar cuatro partes dentro de la página. En la primera insertamos el título y damos
información al usuario; la segunda agrupa las listas de regiones y provincias y los radio button; la
tercera engloba los mensajes de número de visitas y elemento favorito, y la cuarta incluye al botón
de regreso a la página principal y al hiperenlace para invalidar la sesión.
• :
combo de regiones declarado como un selectOneMenu, con lo que
tendremos un menú desplegable del que sólo podremos seleccionar un elemento al mismo tiempo.
El valor seleccionado se almacena en la propiedad regionSeleccionada del bean exCmBean y
declaramos el atributo onchange para que el formulario sea procesado cuando se seleccione un
nuevo valor del menú.
• : etiqueta core para rellenar los valores
del menú de regiones de forma dinámica a partir de la propiedad regionesEnCombo del bean
exCmBean.
• > mpRegionesProvincias =
new TreeMap>();
private String regionSeleccionada = "Aragón";
private String[] provinciasSeleccionadas;
/* Propiedades radio buttons */
private int radioBtSelected;
private String mensajeFavorito;
/**
* Aprovechamos para inicializar las propiedades necesarias
*/
public EjemplosCompBean() {
SortedSet sAux = new TreeSet();
sAux.add("Cáceres");
sAux.add("Badajoz");
mpRegionesProvincias.put("Extremadura", new TreeSet(sAux));
sAux.clear();
sAux.add("Lleida");
sAux.add("Barcelona");
sAux.add("Tarragona");
sAux.add("Girona");
mpRegionesProvincias.put("Catalonia", new TreeSet(sAux));
sAux.clear();
sAux.add("Huesca");
sAux.add("Zaragoza");
sAux.add("Teruel");
mpRegionesProvincias.put("Aragón", new TreeSet(sAux));
}
/* Métodos rellena combos */
public String getRegionSeleccionada() {
return regionSeleccionada;
}
public void setRegionSeleccionada(String regionSeleccionada) {
this.regionSeleccionada = regionSeleccionada;
}
public String[] getProvinciasSeleccionadas() {
return provinciasSeleccionadas;
}
public void setProvinciasSeleccionadas(String[] provinciasSeleccionadas) {
this.provinciasSeleccionadas = provinciasSeleccionadas;
}
public Collection getRegionesEnCombo() {
return mpRegionesProvincias.keySet();
}
public Collection getProvinciasEnCombo() {
return mpRegionesProvincias.get(this.regionSeleccionada);
}
/* Métodos radio buttons */
public int getRadioBtSelected() {
return radioBtSelected;
}
public void setRadioBtSelected(int radioBtSelected) {
this.radioBtSelected = radioBtSelected;
}
public void nuevoRBSeleccionado(ValueChangeEvent event) {
int actualValue = (Integer) event.getNewValue();
switch (actualValue) {
case 10: mensajeFavorito = "Mi color favorito es el ¡rojo!";
break;
case 15: mensajeFavorito = "Mi canción favorita es Nutshell";
break;
case 22: mensajeFavorito = "Mi libro preferido es... no sé... hay tantos...";
break;
}
}
public String getMensajeFavorito() {
return mensajeFavorito;
}
}
Declaramos el ManagedBean asignándole un alias y especificando que su ámbito va a ser de petición.
Los datos de regiones y sus provincias los vamos a almacenar dentro de un mapa en el que la clave
es el nombre de la región y el valor un conjunto ordenado con los nombres de sus provincias.
Aprovechamos el constructor del bean para inicializar este mapa.
El único método no trivial en este código es el oyente de cambio de valor ( public void
nuevoRBSeleccionado(ValueChangeEvent event)). Como explicamos en el capítulo sobre el ciclo de
vida de una petición JSF los eventos de cambio de valor son procesados antes de que se hayan
actualizado las propiedades relacionadas en el bean correspondiente. Es por ello que en este caso no
podríamos hacer referencia a la propiedad regionSeleccionada, pues el valor devuelto sería el valor
antiguo, y no el seleccionado actualmente. Sin embargo, el objeto Event recibido sí que contiene ese
valor actualizado, y es de donde lo obtenemos para poder actualizar el mensaje correspondiente.
Managed Bean de sesión:
@ManagedBean(name="sesBean")
@SessionScoped
public class SessionManagedBean {
private int accesosAPagina = 1;
public int getAccesosAPagina() {
return accesosAPagina;
}
public String salir() {
accesosAPagina++;
return "index";
}
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/index.xhtml?faces-redirect=true";
}
}
Este bean lo utilizamos para mantener el número de accesos a esta página en esta sesión y procesar
los eventos action asociados al botón y al hiperenlace. Hemos optado por utilizar un bean diferente
para mostrar la posibilidad de utilizar diferentes Managed Beans en una misma página; podríamos
haber optado por gestionar todos los componentes de la página en un único bean, o incluso utilizar
un mismo bean para diferentes páginas.
Este bean es muy sencillo; resaltamos el uso de la anotación @SessionScoped para mantener la
misma instancia del bean durante llamadas y mantener de esta manera el valor de la propiedad
accesosAPagina; el acceso a 'métodos globales' de una aplicación JSF mediante la clase
FacesContext, en este caso para poder invalidar la sesión; y la utilización del parámetro f acesredirect=true cuando queremos que el resultado de un action sea una redirección, y no un reenvío.
Hablaremos más detalladamente sobre los posibles ámbitos de un bean en el capítulo dedicado a los
Managed Beans.
Ciclo de vida de una petición JSF
JSF establece un ciclo de vida de múltiples fases para cada petición JSF que se envía al servidor.
Estas fases incluyen todas las tareas que se realizan en el servidor desde que la petición es recibida
hasta que se envía la respuestas. Cada una de las fases tiene una funcionalidad bien definida, y
aunque no es necesario conocerlas para implementar aplicaciones JSF, un conocimiento mínimo de
las distintas fases puede resultar de gran ayuda al desarrollador de aplicaciones.
Antes de explorar cada una de las fases, hablemos de los elementos involucrados y las tareas que es
necesario realizar en el servidor durante el procesamiento de la petición y la respuesta.
• La información que envía el cliente al servidor en una petición http está siempre codificada en
formato de String (a menos que se trate de un fichero adjunto). Es necesario convertir estas
cadenas de texto a los tipos reales con los que trabaja nuestra aplicación. Para ello JSF define una
serie de conversores estándar que pueden ser ampliados en caso de que determinados valores
necesiten una conversión más personalizada.
• JSF trabaja en el lado servidor con un árbol de componentes que representa la página facelet
(vista) que ha enviado el cliente. Este árbol de componentes se crea en la primera petición sobre
esa página, y se reutiliza en subsiguientes peticiones. La forma de saber si estamos en la primera
petición es por el método http de petición. Cuando es una petición get JSF asume que se trata de
la primera petición del ciclo para esa página, y crea el árbol de componentes asociado (un objeto
componente en el lado servidor por cada componente declarado mediante etiquetas en el facelet).
Si se trata de una petición post JSF recupera el árbol de componentes que creó previamente para
dicha página.
Si pasamos a otra página, el ciclo comienza de nuevo y el árbol actual se destruye.
• Una vez que se ha copiado la información que envía el cliente a los componentes del árbol, los
datos se validan y se convierten. Si no se produce ningún error estos datos ya convertidos se
almacenan en las propiedades de los ManagedBean correspondientes. Si existe algún error de
conversión o validación el framework vuelve a dibujar la página facelet original con los valores
contenidos en el árbol de componentes, para que puedan ser mostrados al cliente y éste corrija los
datos erróneos.
Como ya hemos comentado, JSF actúa de forma diferente ante una petición get y una petición post.
En el primer caso el árbol de componentes es creado de cero a partir de los componentes definidos
en la página facelet. Además en este caso únicamente se ejecutan las fases Restore View y Render
Response, mientras que ante una petición post el framework recupera el árbol de componentes
previamente creado e intenta ejecutar todas las fases del ciclo de vida.
Restore view phase
La fase de Reconstrucción de la Vista es la fase inicial en el procesamiento de una petición JSF.
Durante esta fase, la implementación JavaServer Faces construye el árbol de componentes de la
página facelet, lo almacena en el FacesContext, y conecta los manejadores de eventos, validadores
y conversores a los distintos componentes del árbol.
Si se trata de una petición inicial (get), el framework JSF crea una vista vacía y avanza
directamente a la fase Render Response (Generación de la Respuesta), donde la vista se rellenará
con los componentes correspondientes.
Si se trata de un postback la vista ya existe en el FacesContext. En este caso, simplemente se
recupera y se actualiza con la información almacenada en el cliente o el servidor.
Apply Request Values Phase
En esta fase cada parámetro que envía el cliente se copia al componente correspondiente del árbol,
utilizando el método decode de cada componente, y se realizan las conversiones. Si hay
componentes de la página con un valor de true para su atributo immediate, la validación y
procesamiento de eventos de dichos componentes se realizan en esta fase. Si se produce algún error
de conversión o validación se genera un mensaje de error asociado con el componente
correspondiente y se encola en el FacesContext. Este mensaje se mostrará durante la fase
Renderizar la Respuesta.
Al final de esta fase, los componentes del árbol tienen sus valores actualizados, y puede haber
nuevos mensajes y eventos encolados.
Process Validations Phase
Durante esta fase se procesan todas las validaciones registradas en el árbol de componentes. Si un
valor incumple una regla, la implementación JavaServer Faces añade un mensaje de error al
FacesContext y el ciclo de vida avanza directamente hasta la fase Rendering Response para que la
página sea dibujada de nuevo incluyendo los mensajes de error. Esta alteración del flujo de
ejecución se produce también si hubo algún error de conversión en la fase Apply Request Values.
Si la validación de un componente es correcta, su atributo immediate es false y tiene asociado un
valueChangeListener, este valueChangeListener se invoca en este momento y a continuación se
realiza la validación del siguiente componente.
Es importante resaltar este hecho: los eventos valueChange, cuando immediate es igual a false, son
procesados en esta fase, cuando aún no se han actualizado los valores del modelo, hecho que se
produce en la fase siguiente. Por ello, si necesitamos acceder al valor actual del componente lo
haremos a través del objeto Event que recibimos como parámetro del método oyente.
Existe otro mecanismo para resolver este problema que consiste en encolar de nuevo el evento y
configurarlo para que se procese durante la fase de invocación de la aplicación. Más abajo
ofrecemos una discusión detallada al respecto.
Update Model Values Phase
Una vez que la implementación JavaServer Faces determina que los datos son válidos, recorre el
árbol de componentes y actualiza los valores del modelo con los valores pasados en la petición. La
implementación de JSF actualiza únicamente las propiedades de un bean referenciadas por atributos
value. Si el dato local no se puede convertir a los tipos especificados por los atributos del bean, el
ciclo de vida avanza directamente a la fase Render Response para redibujar la página mostrando los
errores de forma similar a lo que sucede cuando hay errores de validación.
Invoke Application Phase
Durante esta fase, la implementación JavaServer Faces maneja cualquier evento a nivel de
aplicación, como enviar un formulario o enlazar a otra página. A continuación transfiere el control a
la fase Render Response.
Render Response Phase
En esta fase el framework construye la vista y delega la responsabilidad de formatear la respuesta
en la tecnología correspondiente (normalmente páginas facelets).
Si se trata de una petición inicial se añaden los componentes representados en la página al árbol de
componentes.
Si la petición es un postback y se produjo algún error en las fases Apply Request Values, Process
Validations, o Update Model Values, se dibuja de nuevo la página original. Si ésta contiene alguna
etiqueta h:message o h:messages, se muestran los mensajes de error de la cola de mensajes.
Tras renderizar el contenido de la vista se graba el estado de la respuesta para poder acceder a ella
posteriormente.
El atributo immediate
El atributo immediate determina la fase en la que se procesan los eventos asociados a un
determinado componente. Como sabemos, hay dos tipos de eventos de aplicación: eventos action y
eventos valueChange. Por la arquitectura intrínseca Html, cuando modificamos el valor de un
componente en la página ésta no es enviada al servidor para su procesamiento a menos que lo
indiquemos explícitamente en su código mediante javascript (mediante el atributo
onclick=”submit()”), cosa que no sucede con los eventos action. Es decir, cuando pulsamos un
botón o un hiperenlace únicamente se lanza el evento action asociado a ese botón o hiperenlace,
pero pueden lanzarse al mismo tiempo eventos valueChange de componentes que hayamos
modificado sin enviar el formulario. De la misma manera, si al modificar el valor de un componente
enviamos el formulario explícitamente, no va a haber ningún evento action a procesar, pero puede
haber otros eventos valueChange de componentes modificados previamente.
El procesamiento del ciclo de fases de la página Jsf depende entonces de las siguientes reglas:
•
para cualquier componente que lance eventos action, si el valor de su atributo immediate es
false, el procesamiento de dicho evento se realizará durante la fase Invoke Application
(primero se ejecutará el método asociado a su atributo actionListener y a continuación el
asociado a su atributo action).
•
para cualquier componente que lance eventos action, si el valor de su atributo immediate es
true, el procesamiento de dicho evento se realizará durante la fase Apply Requests Values,
y a continuación pasaremos directamente a la fase de Render Response, saltándonos el resto
de fases y por consiguiente el procesamiento de eventos valueChange que no tengan un valor
de su atributo immediate=”true”.
•
para cualquier componente que lance eventos valueChange, si el valor de su atributo
immediate es false, el procesamiento de dicho evento se realizará durante la fase Process
Validations. Un componente de este tipo no tiene su propio 'ciclo de fases', es decir, en esta
fase se valida el componente, si esta validación es correcta se ejecuta su listener asociado y a
continuación pasamos al siguiente componente a validar. Cuando todos los componentes
apropiados han sido validados y se han procesado los eventos correspondientes, si no se ha
producido ningún error de validación continuamos el flujo de procesamiento y pasamos a la
fase Update Model Values; si se han producido errores de validación se encolan los mensajes
correspondientes y pasaríamos directamente a la fase Render Response.
•
para cualquier componente que lance eventos valueChange, si el valor de su atributo
immediate es true, la validación del componente y el procesamiento de dicho evento se
realizarán durante la fase Apply Requests Values. Esto se realiza para todos los
componentes cuyo valor haya sido modificado y tengan un valor de immediate igual a true.
Luego se continua el procesamiento de la petición según las reglas ya explicadas, es decir, si
ha habido algún error de conversión/validación, pasamos directamente a la fase Render
Response, y si no lo ha habido, dependemos de en qué circunstancias se haya enviado el
formulario:
◦ Si el formulario se ha enviado de forma explícita al modificar un valor, pasamos a la fase
de Process Validations, donde se procesarán las validaciones de componentes con un
valor de atributo immediate igual a false.
◦ Si se ha enviado por la pulsación de un botón o un hiperenlace y el atributo immediate de
este componente es true, se ejecuta su action correspondiente y pasamos directamente a
la fase de Render Response, por lo que NO se validan el resto de componentes de la
página ni se procesan otros eventos que hayan sido lanzados.
◦ Si se ha enviado por la pulsación de un botón o un hiperenlace y el atributo immediate de
este componente es false, pasamos a la fase de Process Validations y se continúa el
ciclo de fases sin ninguna alteración.
Ejemplos reales de uso de immediate=”true”
•
Si se usa únicamente en componentes UIInput, la validación de estos componentes se
realizará durante la fase de Apply Request Values. Esto es útil cuando queramos priorizar la
validación de determinados componentes. Cuando la conversión o validación de uno de
estos componentes falle, los componentes cuyo valor de immediate sea false no serán
convertidos ni validados, por lo que sólo se mostrarán los mensajes de error de los
componentes priorizados.
•
Si se usa únicamente en componentes UICommand, pasaremos directamente de la fase Apply
Request Values a la fase Render Response. Útil cuando queremos evitar totalmente el
procesamiento del formulario (por ejemplo en botones Cancel o Back).
•
Si se usa tanto en componentes UIInput como en UICommand, pasaremos directamente de la
fase Apply Request Values a la fase Render Response, pero se realizará la conversión,
validación y procesamiento de eventos de los componentes UIInput modificados cuyo valor
de immediate sea true. Usaremos esto para omitir el procesamiento de todo el formulario
excepto para determinados campos. Por ejemplo, un botón de Olvidé mi contraseña en un
formulario con un campo de Contraseña required pero no immediate.
Pushback de eventos valueChange
Un
es invocado durante la fase de Apply Request Values, si el atributo
immediate del componente que lanzó el evento tiene un valor de true, o durante la fase Process
Validations si el valor de immediate es false. En ambos casos el método oyente es invocado antes
de la fase Update Model Values, es decir, antes de que se actualice la propiedad asociada al
componente con el nuevo valor de éste. Por ello, si necesitamos acceder a ese valor nuevo debemos
recuperarlo a partir del evento que se nos pasa como parámetro, como ya hemos visto en algún
ejemplo:
valueChangeListener
public void nuevoRBSeleccionado(ValueChangeEvent event) {
int actualValue = (Integer) event.getNewValue();
}
switch (actualValue) {
...
Otra solución es retrasar (pushback) el procesamiento del evento a una fase más avanzada en la que
ya se haya actualizado el modelo:
public void nuevoRBSeleccionado(ValueChangeEvent event) {
}
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return;
}
int actualValue = radioBtSelected;
...
En este código le preguntamos al evento por la fase en la que está siendo procesado, y si es
diferente de Invoke Application le cambiamos el identificador de fase y lo encolamos de nuevo. De
esta manera nos aseguramos de que se procese durante la fase indicada.
¿Cuándo usar un método u otro? Bueno, eso depende de nuestra intención. La primera solución nos
asegura que el código del oyente se ejecute existan o no errores de validación en otros componentes
de la página. La segunda solución nos asegura que el código del oyente se ejecuta sólo si no existen
errores de validación en ningún componente y cuando ya se han actualizado los valores del modelo.
Objetos implícitos en una página jsf
- applicationScope: A Map of the application scope attribute values, keyed by attribute name
- cookie: A Map of the cookie values for the current request, keyed by cookie name
#{cookie.userCookie.value} or
#{cookie["userCookie"].value}
- facesContext: The FacesContext instance for the current request
#{facesContext.externalContext.session.id}
- header: A Map of HTTP header values for the current request, keyed by header name
#{header.Accept} or #{header["Accept"]}
#{header["Accept-Encoding"]}
- headerValues: A Map of String arrays containing all the header values for HTTP headers in the
current request, keyed by header name
- initParam: A Map of the context initialization parameters for this web application
- param: A Map of the request parameters for this request, keyed by parameter name
#{param.custID}
- paramValues: A Map of String arrays containing all the parameter values for request parameters in
the current request, keyed by parameter name
- requestScope: A Map of the request attributes for this request, keyed by attribute name
- sessionScope: A Map of the session attributes for this request, keyed by attribute name
- view: The root UIComponent in the current component tree stored in the FacesRequest for this
request
Validación
Validadores estándar:
Validator Class
Tag
Function
BeanValidator
validateBean
Registers a bean validator for the component.
DoubleRangeValidator validateDoubleRange Checks whether the local value of a component is within a certain range.
The value must be floating-point or convertible to floating-point.
LengthValidator
validateLength
Checks whether the length of a component’s local value is within a
certain range. The value must be a java.lang.String.
LongRangeValidator
validateLongRange
Checks whether the local value of a component is within a certain range.
The value must be any numeric type or String that can be converted
to a long.
RegexValidator
validateRegEx
Checks whether the local value of a component is a match against a
regular expression from the java.util.regex package.
RequiredValidator
validateRequired
Ensures that the local value is not empty on
an javax.faces.component.EditableValueHolder compo
nent.
Si necesitamos validación personalizada la podemos implementar a través de métodos de beans o
mediante un validador propio.
1. A través de métodos de beans:
public void validatePlayer3(FacesContext context, UIComponent component, Object
value) throws ValidatorException {
String selectedRadio = (String) value;
String msg =
"V3-- Since you indicated that you play Tennis, Please enter Club Name.";
String clubNameFromComp = (String) clubNameBind.getSubmittedValue();
if ((clubNameFromComp == null || "".equalsIgnoreCase(clubNameFromComp)) &&
"Y".equalsIgnoreCase(selectedRadio)) {
throw new ValidatorException(
new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg));
}
}
…
…
2. Mediante un validador propio:
@FacesValidator("emailValidator")
public class EmailValidator implements Validator
// Declaración e inicialización del pattern
…
@Override
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
matcher = pattern.matcher(value.toString());
if (!matcher.matches()) {
FacesMessage msg =
new FacesMessage("E-mail validation failed.",
"Please provide E-mail address in this format: abcd@abc.com");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
}
throw new ValidatorException(msg);
}
}
…
Conversores
Tenemos los conversores estándar:
...
Y conversores personalizados:
@FacesConverter("pacienteConverter")
public class PacienteConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component,
String newValue) throws ConverterException {
try {
ConsultasDAO dao = (ConsultasDAO)
context.getApplication().getVariableResolver().
resolveVariable(context, "daoIbatis");
Paciente paciente = dao.getPacienteXId(Integer.parseInt(newValue));
return paciente;
} catch (Exception e) {
((ServletContext) context.getExternalContext().getContext()).
log("Error en el getAsObject de PacienteConverter", e);
throw new ConverterException(e);
}
}
public String getAsString(FacesContext context, UIComponent component,
Object value) throws ConverterException {
if (value==null) return "";
try {
Paciente paciente = (Paciente) value;
return Integer.toString(paciente.getId());
} catch (Exception e) {
((ServletContext) context.getExternalContext().getContext()).
log("Error en el getAsString de PacienteConverter", e);
throw new ConverterException(e);
}
}
}
…
Ajax y jsf
Ejemplo:
En este ejemplo el execute=”@this” es redundante ya que es su valor por defecto. listaEmpleados
es el id de un
profe.jsfbasic.beans.ResumeBean
I18N
Una opción es utilizar el faces-config.xml
resources.mensajes
msg
resources.ApplicationMessages
en
es
de
fr
otra, cargar el fichero de recursos a nivel individual en la página
La diferencia entre resource-bundle y message-bundle es que en el segundo
únicamente puedo sustituir mensajes de error por defecto, el primero
es el que referencio directamente desde las jsf o desde código:
Los mensajes de error estándar se encuentran en la especificación de jsf,
capítulo 2.5.2.4, por ejemplo
javax.faces.component.UIInput.REQUIRED
* Acceder a los recursos idiomáticos desde código para mensajes de error en la página asociados a
un id:
FacesContext context = FacesContext.getCurrentInstance();
ResourceBundle bundle =
ResourceBundle.getBundle("resources.mensajes",
context().getViewRoot().getLocale());
String message = bundle.getString(key);
context().addMessage("logonForm:mailboxHost",
new FacesMessage(FacesMessage.SEVERITY_ERROR, message, message));
...
Si no asociamos un mensaje a ningún componente, se considera un mensaje global (se muestra con
la etiqueta h:messages, siempre que el atributo for no esté definido):
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(message));
In addition to Daniel's answer you could always use the label attribute for your input components to
remove the client-id (j_idt10:userNo:) from the error message.
E.g. with
will produce:
User number: Validation Error: Specified attribute is not between the expected values
of 3 and 6.
The label attribute can be an el expression as well to change this part of the error message
dynamically.
* Cambiar el locale para la aplicación entera:
El locale en jsf se recupera por defecto por request, no por sesión como en struts. Por ello hay que
realizar dos pasos. El primero, cambiar el locale en la UIViewRoot y almacenarlo en un bean de
sesión:
FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale("ua"));
// almacenarlo en un bean de sesión
el segundo, en todas las páginas añadir el atributo locale a la vista:
localeBean sería el bean de sesión en el que hemos almacenado el locale en el paso 1
Misc
* Saber qué botón hemos pulsado en un validador o conversor:
Map params = context.getExternalContext().getRequestParameterMap();
if (params.containsKey("formId:button1")) {
// Button 1 is pressed.
}
else if (params.containsKey("formId:button2")) {
// Button 2 is pressed.
}
* Almacenar atributos en la sesión:
FacesContext.getCurrentInstance().
getExternalContext().getSessionMap().
put("name", event.getNewValue());
* Diferencia entre binding y value: el primero enlaza con una propiedad que representa el
componente (HtmlInputText, HtmlOutputLabel...) mientras el segundo enlaza con una propiedad
simple que almacena el valor.
* Acceder a beans desde código:
javax.faces.context.FacesContext fc =
javax.faces.context.FacesContext.getCurrentInstance();
Object v = fc.getApplication().getVariableResolver().
resolveVariable(fc, "facesBean");
pageContext.setAttribute("beanName", v);
//To reset session bean
FacesContext
.getCurrentInstance()
.getApplication()
.createValueBinding( "#{yourBeanName}").
setValue(FacesContext.getCurrentInstance(), null );
//To get session bean reference
Object obj = FacesContext
.getCurrentInstance()
.getApplication()
.createValueBinding("#{yourBeanName}")
.getValue(FacesContext.getCurrentInstance());
YourSessionBean bean = (YourSessionBean)obj;
* Para pasar a otra fase de forma explícita
FacesContext.getCurrentInstance().renderResponse();
* El papel del atributo rendered: parece que cuando jugamos con el atributo rendered, éste va a
influir en que se procesen las validaciones o no del componente asociado. Si rendered=false en la
página resultante y el componente que hace submit del formulario tiene immediate=true, entonces el
componente no se valida
* Para tener diferentes archivos de configuración de faces:
A resource named /META-INF/faces-config.xml in any of the JAR files in the web application's
/WEB-INF/lib/ directory and in parent class loaders. If a resource with this name exists, it is
loaded as a configuration resource. This method is practical for a packaged library containing some
components and renderers.
A context initialization parameter, javax.faces.application.CONFIG_FILES, that specifies one or
more (comma-delimited) paths to multiple configuration files for your web application. This
method will most likely be used for enterprise-scale applications that delegate to separate groups the
responsibility for maintaining the file for each portion of a big application.
A resource named faces-config.xml in the /WEB-INF/ directory of your application. This is the way
most simple applications will make their configuration files available.
Managed Beans
Aplicación de Gestión
Vamos a desarrollar una aplicación de gestión básica para poner en práctica algunos de los
conocimientos que hemos adquirido. La aplicación va a consistir en una página de mantenimiento
de empleados que va a tener este diseño:
Cuando seleccionemos alguno de los empleados de la lista, sus datos completos aparecerán en los
campos de texto. Cuando pulsemos el botón de Listar, aparecerá una lista de todos los empleados
con todos sus datos y el botón cambiará su valor a Ocultar. Cuando volvamos a pulsarlo tomará de
nuevo el valor Listar y desaparecerá la lista de empleados. Los botones de Insertar, Modificar o
Eliminar realizan la función correspondiente, con la particularidad de que Modificar y Eliminar no
determinan el empleado a modificar o eliminar según el empleado seleccionado, sino que consultan
para ello el valor del campo de texto Cif. Además tenemos validaciones condicionales en función
del evento que se ha lanzado: si se ha seleccionado un empleado nuevo a través de la lista no
tenemos que realizar ninguna validación, al igual que si se pulsa el botón de Listar. Si pulsamos el
botón de Eliminar sólo es obligatorio el campo Cif, mientras que al pulsar Insertar o Modificar
todos los campos son obligatorios.
También haremos uso de las facilidades de i18n de JavaServer Faces para proporcionar acceso a
nuestra aplicación en otros idiomas:
Para esta aplicación vamos a hacer uso del estándar cdi de JEE. Como utilizamos Tomcat como
contenedor, necesitamos utilizar una implementación externa de dicho estándar. Vamos a utilizar
para ello Weld, de JBoss.
Necesitamos añadir entonces las siguientes librerías a nuestro proyecto:
- cdi-api-1.2.jar
- javax.inject-1.jar
- javax.el-api-3.0.0.jar
- javax.interceptor-api-1.2.jar
- weld-servlet-2.2.13.Final.jar
Las dos primeras deben estar en la ruta de clases de nuestro entorno para poder compilar contra
ellas.
Source Exif Data: File Type : PDF
File Type Extension : pdf
MIME Type : application/pdf
PDF Version : 1.4
Linearized : No
Page Count : 48
Language : es-ES
Creator : Writer
Producer : LibreOffice 6.0
Create Date : 2019:01:14 11:23:50+01:00
EXIF Metadata provided by EXIF.tools
Navigation menu
Versions of this User Manual: