Navegando entre PageFragments de un TaskFlow utilizando JavaScript

Los TaskFlows en ADF son componentes que proporcionan un sin número de funcionalidades que permiten tener una separación de interfaces gráficas junto con funcionalidad lógica.  Estos pueden ser empaquetados  como librerías y ser consumidos dentro de otras aplicaciones ADF de manera transparente.
Existe un tipo de Taskflow llamado bounded, los cuales están compuestos por una serie de vistas (páginas), denominadas pageFragments o fragmentos – estos con extensión jsff. El caso de estudio que desarrollamos en este artículo parte de una interfaz en la cual tenemos una serie de botones para mostrar las distintas vistas del taskflow.

image image

En lugar de hacer click en cada uno de los botones, queremos que una vez el mouse este posicionado sobre el botón se despliegue la vista correspondiente. Los pasos para implementar esta funcionalidad son:
  • Crear el taskflow con las vistas
  • Implementar la navegación entre vistas
  • Adicionar JavaScript a las vistas para capturar el movimiento del mouse
  • Implementar la lógica de navegación del lado del servidor una vez se capture el evento

Creando el Taskflow con las vistas

image
Nuestro taskflow en este caso contiene 2 vistas y sus respectivos caminos de navegación definidos.

Implementando navegación entre vistas

En cada una de nuestras vistas vamos a tener una barra de botones en la parte superior los cuales vamos a utilizar para la navegación entre páginas.
image

Insertando JavaScript y capturando eventos de los componentes ADF Faces

El codigo de una de las páginas queda de la siguiente forma:
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
xmlns:f="http://java.sun.com/jsf/core">
<af:panelStretchLayout id="psl1">
<f:facet name="center">
<af:resource type="javascript">
function callServer(event){
var source = event.getSource();
AdfCustomEvent.queue(source,"jsServerListener",{},true);
}
</af:resource>
<!-- id="af_one_column_stretched"   -->
<af:decorativeBox theme="dark" id="db1">
<f:facet name="center">
<af:decorativeBox theme="medium" id="db2">
<f:facet name="center">
<af:panelGroupLayout id="pgl1" layout="vertical">
<af:toolbar id="t1">
<af:commandToolbarButton text="Go Locations"
id="ctb1"/>
<af:commandToolbarButton text="Go Departments"
id="ctb2"
binding="#{backingBeanScope.locationBean.departmentButton}">
<af:clientListener method="callServer"
type="mouseOver"/>
<af:serverListener type="jsServerListener"
method="#{backingBeanScope.locationBean.goDepartment}"/>
</af:commandToolbarButton>
</af:toolbar>


Los 3 elementos destacables dentro del código son:


<af:source> el cual es utilizado para insertar JavaScript o hacer referencia a algún archivo JavaScript que se esté utilizando. La función hace un llamado al serverListener definido dentro del botón.


<af:clientListener> Lo utilizamos para capturar el evento del Mouse y ejecutamos una función en JavaScript


<af:serverListener> utilizado para invocar código del lado del servidor, importante notar la notación utilizada para hacer referencia al método del backing bean.


Implementando la lógica de navegación del lado del servidor una vez se capture el evento



En el backing bean implementamos nuestro método goDepartment() en este casó he creado un backing bean por vista y cada uno va a contener su método. Sin embargo creo que esto se puede optimizar para crear un método genérico que sea accedido por ambas vistas.

El código del método queda de la siguiente forma:


public void goDepartment(ClientEvent clientEvent) {
// force navigation to quotes fragment
FacesContext ctx = FacesContext.getCurrentInstance();
ELContext elctx = ctx.getELContext();
MethodExpression me = ctx.getApplication().getExpressionFactory().createMethodExpression(elctx,"#{backingBeanScope.locationBean.goDept_action}",String.class,new Class[]{});
departmentButton.setActionExpression(me);
ActionEvent actionEvt = new ActionEvent(departmentButton);
departmentButton.queueEvent(actionEvt);
}


Este método crea una expresión la cual evalúa hacia un método dentro del mismo bean el cual retorna el String con el caso de navegación. Luego a través del método queueEvent se hace que la acción de navegación sea ejecutada.

Si alguno sabe de una forma más optima de llevar a cabo el mismo procedimiento la puede compartir. Estuve probando con el navigationHandler pero al parecer no funciona con fragmentos de páginas.


Detalles Adicionales Importantes



Dado que estamos dentro del contexto de un taskflow, tengan en cuenta que TODO backing bean debe ser definido dentro de este mismo contexto. Note que en el momento de definir un método que va a un backing bean dentro de una página o un fragmento, la definición va a ir directamente al adfc-config, lo cual va a causar problemas (he creado un bug al respecto).


El scope de los backing beans que estén dentro del taskflow va a ser backing bean. Al principio, el haber dejado request nos causó conflictos con las instancias el taskflow que se fueron creando.


En un siguiente post explicaré como empaquetar este taskflow y reutilizarlo dentro de otras aplicaciones como una librería.