> Manuales > Ayudas técnicas

Terminamos este pequeño manual sobre JSF comenzando con la Internacionalización (i18n).

La internacionalización nos va ha permitir mostrar la aplicación según el idioma del usuario. Para ello en el fichero faces-config.xml vamos a especificar que localizaciones soporta la aplicación, y cual es la localización por defecto:

<application>
   <locale-config>
      <default-locale>es</default-locale>
      <supported-locale>en</supported-locale>
   </locale-config>
</application>


A continuación mostramos un ejemplo de uso de i18n, que podemos encontrar en el fichero customize.jsp:

<span class=codigo>
...
<html>
<f:view>
<f:loadBundle basename="MessageResources" var="msg"/>
<head>
    <title>${msg.customize_title}</title>
</head>
<body>
...

En el ejemplo se puede ver como lo primero que se hacer es cargar el fichero de recursos. El sistema se encargará de seleccionar el fichero apropiado según el lenguaje, y si se trata de un lenguaje no soportado se seleccionará el idioma por defecto.

Una vez cargado el fichero de recursos es tan facil usarlo como “${msg.customize_title}” donde “msg” es el identificador que le hemos dado al fichero de recursos y “cutomize_title” es la calve del mensaje que queremos mostrar.

Recuperando los valores del formulario

Desde el punto de vista del interfaz (customize.jsp) tenemos líneas del estilo:

<h:inputText id="name" value="#{resumeBean.name}" required="true" />

Esto dibujará un campo de entrada y los valores introducidos los guardará en la propiedad “name” del bean “resumeBean”. Este bean es el que hemos definido en el fichero faces-config.xml:

<managed-bean>
   <managed-bean-name>resumeBean</managed-bean-name>
   <managed-bean-class>com.autentia.jsfexample.ResumeBean</managed-bean-class>
   <managed-bean-scope>session</managed-bean-scope>
</managed-bean>


Por supuesto la clase com.autentia.jsfexample.ResumeBean tendrá métodos get/set públicos para acceder a “name”.

Es importante destacar como en el fichero faces-config.xml se define el scope del bean. JSF buscará el bean en ese scope, y si no lo encuentra lo creará y lo guardará en ese scope.

Ojo porque JSF sólo se encarga de crear el bean, pero no de su destrucción, así que todo lo que guardemos en sesión o aplicación allí se quedará hasta que nosotros lo quitemos. Si queremos quitar un bean de la sesión (muy recomendable cuando el objeto ya no es necesario) podemos hacer algo como:

FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getSessionMap().remove("nombreDelBeanEnSesion");


Validación de los campos de entrada Especificar las validaciones

En la vista encontramos cosas como:

<h:inputText id="name" value="#{resumeBean.name}" required="true" />

Se ve como al definir el campo de entrada se está especificando que es obligatorio “required = true”. Otro ejemplo más sería:

<h:inputText id="age" value="#{resumeBean.age}" required="true">
   <f:validateLongRange minimum="16" maximum="64"/>
</h:inputText>


Aquí se puede ver como, además de hacer que el campo sea obligatorio, se está fijando un valor mínimo y un valor máximo. Además como la propiedad “age” del bean “resumeBean” es de tipo “int” también se hará comprobación del tipo del valor introducido por el usuario, es decir, si el usuario no introduce un número, dará un error al validar.

Al igual que en Struts disponemos de multitud de validaciones predefinidas. Además también contamos con API que podemos implementar para hacer nuestras propias validaciones.

Esta forma de especificar las validaciones es más sencilla que en Struts, ya que se hace directamente en la definición del campo de entrada, y no en un fichero separado. Además la validación no está asociada a ningún bean en concreto como pasa en Struts, con lo que resulta más flexible.

Actualmente JSF no dispone de validaciones en cliente con JavaScript, pero esto no es un problema ya que si queremos usar esta funcionalidad siempre podemos usar Apache Commons Validator. Podéis encontrar un ejemplo en http://jroller.com/page/dgeary?entry=added_commons_validator_support_to

Mostrar los errores de la validación

Para mostrar los errores de la validación basta con escribir la siguiente línea donde queremos mostrar los errores:

<x:messages showSummary="false" showDetail="true" style="color: red" />

En nuestro caso estamos usando el componente con funcionalidad ampliada que proporciona MyFaces (x:messages). Este funciona igual que el definido por el estándar, pero al mostrar el error, en vez de aparecer el id del campo de entrada, aparece el valor de la etiqueta asociada. Con esto conseguimos que el mensaje sea más legible para el usuario, ya que se tendrá en cuenta el lenguaje en el que se está viendo la página.

Para escribir la etiqueta del campo de entrada usaremos:

<h:outputLabel for="name" value="#{msg.name}" />

donde el atributo “for” es el identificador del campo de entrada.

Por supuesto, al igual que en Strutrs, podemos redefinir los mensaje de error de las validaciones.

Gestión de eventos y navegación

En este apartado vamos a ver como gestionamos cuando un usuario pulsa un botón o cambia el valor de un campos de entrada, y como afecta ese evento a la navegación de la aplicación.

Con JSF todos los eventos se gestionan en el servidor, pero no hay ninguna restricción para tratar en cliente con JavaScript los eventos que afectan a como se visualiza el interfaz de usuario.

Aceptar el formulario

En nuestro ejemplo encontramos un par de botones, vamos a fijarnos primero en el botón de “Previsualizar”. Este botón sería el que típicamente acepta el formulario, manda los datos al servidor, y nos da paso a la siguiente pantalla.

La línea que define el botón y su comportamiento es:

<h:commandButton value="#{msg.showPreview}" action="#{resumeBean.showPreview}" />

Se puede ver como el atributo “actión” define el método que se ha de invocar. En nuestro ejemplo el método “showPreview” del bean “resumeBean”. Lo importante de este tipo de métodos es que tienen que devolver un String indicando a donde hay que saltar.

public String showPreview() {
   if (isColorSupported && fgColor.equals(bgColor)) {
       return "same-color";
    } else {
      return "success";
   }
}


Esto enlaza con las reglas de navegación que tenemos en el fichero faces-config.xml:

<navigation-rule>
    <from-view-id>/customize.jsp</from-view-id>
   <navigation-case>
       <from-outcome>same-color</from-outcome>
       <to-view-id>/same-color.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
       <from-outcome>success</from-outcome>
       <to-view-id>/show-preview.jsp</to-view-id>
    </navigation-case>
</navigation-rule>


Si estamos en la página “customize.jsp” y el método devuelve el String “same-color” saltaremos a la página “same-color.jsp”. Pero si el método devuelve el String “success” saltaremos a la página “show-preview.jsp”. Como ya adelantábamos, es muy similar al funcionamiento de los forward de Struts, tiene la ventaja de que nuestro bean no tiene que extender de ninguna clase en concreto.

No siempre es necesario invocar a un método para activar la regla de navegación. Esto sólo será necesario para una navegación dinámica donde, dependiendo de alguna condición, saltaremos a una página o a otra. Si la navegación es estática, es decir siempre saltamos a la misma página, no haría falta implementar ningún método, y bastaría con poner:

<h:commandButton value="#{msg.showPreview}" action="success" />

Un evento en un botón

Podemos ver este tipo de eventos como acciones que implican cambios sobre el interfaz de usuario, pero todavía no hacemos la aceptación y procesamiento de los datos del formulario.

Tenemos el ejemplo del botón “Desactivar configuración de color”. Cuando el usuario pulse este botón se desactivarán las listas desplegables que hay sobre él, y cambia el texto del propio botón a “Activar configuración de color”.

<h:commandButton value="#{resumeBean.colorSupportLabel}"
actionListener="#{resumeBean.toggleColorSupport}"
immediate="true" />


Para conseguir que cambie le texto se puede ver como, en vez de usar directamente i18n, le pedimos el valor al propio bean con “#{resumeBean.colorSupportLabel}”.

Con “immediate='true'” conseguimos que no se ejecuten las validaciones.

Con el atributo “actionListener” estamos definiendo quien atenderá el evento cuando se pulse el botón. En nuestro ejemplo es el método “toggleColorSupport” del bean “resumeBean”. Este tipo de métodos no devuelven nada y tienen un único parámetro de tipo “ActionEvent”.

public void toggleColorSupport(ActionEvent event) {
   isColorSupported = !isColorSupported;
}


Este método simplemente cambia el valor de la propiedad “isColorSupported”. El valor de esta propiedad es consultado cuando se va a pintar la lista de selección, en el atributo “disabled”:


   <h:selectOneMenu id="fgColor"
       value="#{resumeBean.fgColor}"
      disabled="#{!resumeBean.colorSupported}">
    <f:selectItems value="#{resumeBean.availableColors}" />
</h:selectOneMenu>


Un evento en un check box

En nuestro ejemplo tenemos un check box que cuando está seleccionado hace que los en vez de los nombres de los colores veamos su codificación RGB (Red Green Blue).

Este caso es un poquito más complicado, ya que la pulsación de un check box no supone el envío del formulario al servidor. En este caso utilizamos:

<h:selectBooleanCheckbox id="changeColorMode"
    valueChangeListener="#{resumeBean.changeColorMode}"
   immediate="true"
   onchange="submit()" />


Nuevamente estamos utilizando un método que atiende el evento del cambio del valor del check box, lo definimos con el atributo “valueChangeListener”. Este tipo de métodos no devuelven nada y tienen un único parámetro de tipo “ValueChangeEvent”.

public void changeColorMode(ValueChangeEvent event) {
   boolean flag = ((Boolean)event.getNewValue()).booleanValue();
   setUsingColorNames(!flag);
}


Volvemos a utilizar “immediate” para que no se ejecuten las validaciones.

Es fundamental el uso de “onchange='submit()'”. Esta es la principal diferencia con el caso anterior, y es lo que nos permite que el procesamiento del evento se haga de forma inmediata una vez el usuario pulsa sobre el check box.

Conclusiones

Si os habéis fijado, con JSF no hemos trabajamos con objetos de tipo HttpSession, HttpServletRequest, ... esto es una ventaja ya que no hay prácticamente acoplamiento entre nuestra aplicación y el hecho de que el interfaz de usuario se a través de HTTP.

La elección de MyFaces como implementación de JSF no es casual. El hecho de que MyFaces sea Software Libre nos permite no perder las ventajas que nos daba Struts en este sentido. Es decir, independencia tecnológica, acceso al código y modificación del mismo si fuera necesario, nulo coste para acceder a esta tecnología, ...

Además la comunidad de Apache está haciendo un estupendo trabajo con MyFaces (como ya nos tienen acostumbrados en el resto de proyectos) ya que además de los componentes del estándar, nos ofrecen una implementación mejorada de los mismos, y unos cuantos controles adicionales al estándar.

Aunque este tutorial no deja de ser una introducción a JSF, habéis podido ver que JSF surge como un framework muy prometedor, y una opción muy recomendable para nuevos desarrollos.

Pero ojo, ningún framework resuelve todas las problemáticas. El ejemplo más sencillo para comprender esta afirmación es pensar en un aplicación web y en un portal. En el primer caso el usuario interactúa con la aplicación introduciendo unos datos y esperando obtener unos resultados, en el segundo caso el usuario puede navegar por distintas pantallas de información, done esta información seguramente cambie a lo largo del tiempo (cada día, semana, mes, ...).

Para las aplicaciones web pueden ser muy convenientes frameworks como JSF, Struts, Spring, ... Pero estos no nos servirán (o será muy trabajoso) para hacer portales. Para este segundo caso sería más adecuado usar gestores de contenidos como Lenya, OpenCMS, ...

Por esto en Autentia solemos recomendar la elección de más de un framewrok, dependiendo de las necesidades.

Alejandro Pérez García

Dir. Implantación y Rendimiento.

Manual