Tutorial JSF 2.2 - Sesión 2: Navegación

La navegación en JSF hace fácil la navegación entre páginas y permite manejar procesamiento adicional que sea necesario al momento de ir entre una página y otra.

Hay dos tipos de navegación: la implícita y la definida por el usuario.
En este post veremos estos dos con un ejemplo simple.

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

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

Navegación implícita


La navegación implícita consiste en indicarle al tag - mediante el atributo action - a qué página se direccionará cuando sea ejecutado. Solo deberá ir el nombre del archivo sin la extensión. Es por ello que se llama "implícito".

Nuestro archivo mensajes.properties
abrir_pagina_1_enlace=Este enlace abrirá la página 1
abrir_pagina_1_texto=Esta es la página 1

Nuestro index.xhtml:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>Facelet Title</title>
        <f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>

    </h:head>
    <h:body>
        <h:form>
            <h:commandLink value="#{msg.abrir_pagina_1_enlace}" action="pagina1" />

        </h:form>
    </h:body>
</html>

Notemos en la línea 13 (además de usar la buena práctica que vimos en el anterior post), el action solo indica el nombre del archivo a mostrar. No incluye la extensión.

Y nuestro archivo pagina1.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>Facelet Title</title>
        <f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
    </h:head>
    <h:body>
        <h1>#{msg.abrir_pagina_1_texto}</h1>
    </h:body>
</html>

Navegación definida por el usuario

Este tipo de navegación se hace usando un archivo de configuración, tal como faces-config.xml usando reglas en formato xml. La estructura de la regla es bastante simple:

  • Se indica en qué página se ejecutará la regla
  • Qué comando y orden se ejecutará
  • Qué página se mostrará.
Creemos los enlaces en la página de inicio (index.xhtml)
                <li>#{msg.navegacion_definida_usuario}
                    <ul>
                        <li><h:commandLink action="page1" value="#{msg.pagina1_enlace}" /></li>
                        <li><h:commandLink action="page2" value="#{msg.pagina2_enlace}" /></li>
                    </ul>
                </li>

Y crearemos dos páginas más navegacion_definida_usuario_pagina1.xhtml y navegacion_definida_usuario_pagina2.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
        <title>#{msg.navegacion_definida_usuario}</title>
    </h:head>
    <h:body>
        <h1>#{msg.navegacion_definida_usuario}</h1>
        <h2>#{msg.pagina1}</h2>

        <h:form>
            <h:commandButton value="#{msg.ir_inicio}" action="index" />
        </h:form>
    </h:body>
</html>

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
        <title>#{msg.navegacion_definida_usuario}</title>
    </h:head>
    <h:body>
        <h1>#{msg.navegacion_definida_usuario}</h1>
        <h2>#{msg.pagina2}</h2>

        <h:form>
            <h:commandButton value="#{msg.ir_inicio}" action="index" />
        </h:form>
    </h:body>
</html>


Sí, el único fue el titulo y el nombre físico del archivo.
Ahora bien ¿cómo se le dice que cuando se llame a page1 vaya a navegacion_definida_usuario_pagina1.xhtml ? Aquí es donde entrar el archivo de configuración. Si lo ejecutamos tal cual, nos aparecerá un mensaje de advertencia.


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


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

Se nos abrirá en el editor el archivo en formato xml. Hagamos clic en el barra superior sobre el botón "PageFlow".


Ahora sí!, algo visual en NetBeans!



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



Y veremos que cada arco tiene un nombre: case1 y case2. Hagamos clic derecho en cada uno de ellos y seleccionemos "Rename..." para cambiarle el nombre a page1 y page2 respectivamente.


Adicionalmente, podemos seleccionar el botón superior "Source"

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
    <navigation-rule>
        <from-view-id>/index.xhtml</from-view-id>
        <navigation-case>
            <from-outcome>page1</from-outcome>
            <to-view-id>/navegacion_definida_usuario_pagina1.xhtml</to-view-id>
        </navigation-case>
        <navigation-case>
            <from-outcome>page2</from-outcome>
            <to-view-id>/navegacion_definida_usuario_pagina2.xhtml</to-view-id>
        </navigation-case>
    </navigation-rule>
</faces-config>

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

Navegación dependiente de un valor

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

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

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

Este es nuestro ManagedBean
package com.apuntesdejava.jsf.controladores;

import java.util.Calendar;
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

/**
 *
 * @author dsilva
 */
@Named("navegacionBean")
@RequestScoped
public class NavegacionBean {

    static final Logger LOGGER = Logger.getLogger(NavegacionBean.class.getName());

    public NavegacionBean() {
        LOGGER.info("Iniciando Bean");
    }

    public String saludar() {
        int hora = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
        if (hora < 12) {
            return "buenos-dias";
        }
        if (hora < 18) { //aquí en Lima, a partir de las 7pm ya es oscuro
            return "buenas-tardes";
        }
        if (hora < 23) {
            return "buenas-noches";
        }
        return null; //todo action=null redirecciona  a la misma página de donde fue invocado
    }
}

Nuestro faces-config.xml va a recibir esas respuestas y redireccionará a cada página según corresponda.
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
   <navigation-rule>
        <from-view-id>/index.xhtml</from-view-id>
        <navigation-case>
            <from-outcome>page1</from-outcome>
            <to-view-id>/navegacion_definida_usuario_pagina1.xhtml</to-view-id>
        </navigation-case>
        <navigation-case>
            <from-outcome>page2</from-outcome>
            <to-view-id>/navegacion_definida_usuario_pagina2.xhtml</to-view-id>
        </navigation-case>
        <navigation-case>
            <from-outcome>buenos-dias</from-outcome>
            <to-view-id>/saludo/maniana.xhtml</to-view-id>
        </navigation-case>
        <navigation-case>
            <from-outcome>buenas-tardes</from-outcome>
            <to-view-id>/saludo/tarde.xhtml</to-view-id>
        </navigation-case>
        <navigation-case>
            <from-outcome>buenas-noches</from-outcome>
            <to-view-id>/saludo/noche.xhtml</to-view-id>
        </navigation-case>
    </navigation-rule> 
</faces-config>

Y crearemos estos saludos en nuestro proyecto. Crearemos tres archivos llamados maniana.xhtml,tarde.xhtml y noche.xhtml. Por orden, los puse dentro de la subcarpeta saludo. Aquí solo mostraré uno de esos archivos, porque los demás serán iguales:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h1>#{msg.saludo_maniana}</h1>
        <h:form>
            <h:commandButton value="#{msg.ir_inicio}" action="/index" />
        </h:form>

    </h:body>
</html>


Y... cuando se invoca desde la página de inicio, se deberá llamar al managedBean seguido del método.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <f:loadBundle basename="com.apuntesdejava.jsf.resources.mensajes" var="msg"/>
        <title><h:outputText value="#{msg.app_title}"/></title>

    </h:head>
    <h:body>
        <h1><h:outputText value="#{msg.app_title}"/></h1>

        <h:form>
          #{msg.navegacion_procesada}<br/>
          <h:commandLink value="#{msg.saludar}" action="#{navegacionBean.saludar}"/>
        </h:form>
    </h:body>
</html>


Y listo! Probemos y veamos la magia!

Antes de terminar...

Los nombres de las reglas de navegación pueden ser cualquiera, a gusto del desarrollador. Pero hay una lista de nombres comunes en las aplicaciones:
  • success
  • failure
  • login
  • no results
Estos lo podrás ver descritos aquí: Configuring Navigation Rules

El código fuente

Como siempre, aquí les ofrezco el código fuente que utilicé para este post.

Y si quereís ver el código fuente sin descargar el proyecto, aquí lo podeís encontrar.


Bibliografía

Naturalmente, toda esta información no fue tomada del aire ni de mi imaginación. Para este post me he basado de: The Java EE 7 Tutorial - Navigation Model.


Hasta el próximo tutorial!
Bendiciones a todos

Comentarios

Entradas más populares de este blog

Groovy: Un lenguaje dinámico y ágil para la Plataforma Java

Cambiar ícono a un JFrame

UML en NetBeans