72134099 richfaces trabajando con tablas

26
RichFaces, trabajando con tablas (1) Introducción El problema de la gestión de tablas se describe en el siguiente esquema: La renderización de tablas que muestran gran cantidad de datos acarrea varios problemas: 1. Acceso a la base de datos. 2. Almacenamiento de los registros como objetos Java en memoria. 3. Renderización de todos los registros en la página que verá el usuario en el navegador. La biblioteca de componentes JavaServer Faces de JBoss llamada RichFaces proporciona varios componentes para el manejo de tablas avanzado, proporcionando paginación, ordenación y filtrado de datos. Estos mecanismos intentan reducir los problemas de renderización provocados por la visualización de demasiados elementos en una tabla simultaneamente, este concepto ha sido identificado mediante el punto 3. Los componentes RichFaces que vamos a utilizar son: <rich:dataTable> <rich:column> <rich:dataScroller> Estos componentes nos permitirán gestionar grandes cantidades de información de forma eficiente:

Upload: julio-cesar-cardenas-narvaez

Post on 04-Aug-2015

375 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: 72134099 RichFaces Trabajando Con Tablas

RichFaces, trabajando con tablas (1)

Introducción

El problema de la gestión de tablas se describe en el siguiente esquema:

La renderización de tablas que muestran gran cantidad de datos acarrea varios problemas:

1. Acceso a la base de datos.

2. Almacenamiento de los registros como objetos Java en memoria. 3. Renderización de todos los registros en la página que verá el usuario en el navegador.

La biblioteca de componentes JavaServer Faces de JBoss llamada RichFaces proporciona varios componentes para el

manejo de tablas avanzado, proporcionando paginación, ordenación y filtrado de datos. Estos mecanismos intentan

reducir los problemas de renderización provocados por la visualización de demasiados elementos en una tabla simultaneamente, este concepto ha sido identificado mediante el punto 3.

Los componentes RichFaces que vamos a utilizar son:

<rich:dataTable>

<rich:column>

<rich:dataScroller>

Estos componentes nos permitirán gestionar grandes cantidades de información de forma eficiente:

Page 2: 72134099 RichFaces Trabajando Con Tablas

Creando tablas

Introducción

El componente rich:dataTable permite crear tablas en RichFaces. Este componente es muy parecido al componente

<h:dataTable> con dos variaciones:

Skinnability: se aplican estilos sobre las tablas de una forma cómoda y sencilla.

Soporte Ajax: este componente es capaz de manejar y generar peticiones y respuestas Ajax.

También existen atributos para la gestión de eventos.

<rich:dataTable ...>

...

</rich:dataTable>

Los atributos más importantes del componente son:

value: la colección de datos que se va a mostrar. Se hace referencia a ésta mediante lenguaje de expresión.

var: nombre de la variable sobre la que se itera.

<rich:dataTable value="#{bean1.datos}" var="factura" ...>

...

</rich:dataTable>

Importante: se supone que el lector maneja JavaServer Faces, por esa razón, debe estar familiarizado con estos atributos,

el lenguaje de expresión, etc.

El componente rich:column

La definición correcta de la tabla se hace habitualmente mediante el uso de otro componente llamado column. Como su

nombre indica, permite crear columnas dentro de la tabla. La información que muestra cada columna se incluye dentro de ella, anidando otro componente. Por ejemplo, si se quiere mostrar un texto se anidará un componente <h:outputText>

<rich:dataTable ...>

<rich:column>

<h:outputText ...></h:outputText>

</rich:column>

...

</rich:dataTable>

Las facetas de la tabla

La definición de título, cabecera y pie de tabla se realiza mediante facetas: header, footer y caption. Como se puede ver en

el ejemplo anterior, se definen cabeceras para cada columna:

Page 3: 72134099 RichFaces Trabajando Con Tablas

<rich:dataTable ...>

<f:facet name="header">

...

</facet>

...

</rich:dataTable>

Ejemplo

Se pasa a renderizar una tabla de facturas. Éstas se encuentran almacenadas en una lista definida en un mbean de

JavaServer Faces (debe estar declarado en el fichero de configuración de JSF).

...

public class MBean1 {

...

// Lista de facturas que muestra la tabla

public List<Factura> facturas;

...

}

La clase Factura tiene atributos para cada información que se quiere manejar de la factura:

...

public class Documento{

private String concepto;

private String codigo;

private double importe;

...

}

La tabla en la vista se construye de la siguiente forma:

...

<rich:dataTable value="#{mbean1.facturas}" var="f" ...>

<rich:column>

<f:facet name="header">

Page 4: 72134099 RichFaces Trabajando Con Tablas

<h:outputText value="Concepto" />

</f:facet>

<h:outputText value="#{f.concepto}" />

</rich:column>

<rich:column>

<f:facet name="header">

<h:outputText value="Código de la factura" />

</f:facet>

<h:outputText value="#{f.codigo}" />

</rich:column>

<rich:column>

<f:facet name="header">

<h:outputText value="Importe" />

</f:facet>

<h:outputText value="#{f.importe}" />

</rich:column>

</rich:dataTable>

...

Paginando tablas

El proceso de paginación se lleva a cabo mediante el componente <rich:datascroller>. Para realizar la paginación se

utilizan peticiones Ajax. Este componente debe incluirse dentro del pie de la tabla padre o relacionarla mediante su

atributo for:

- Dentro del pie:

<rich:dataTable ...>

<rich:column>

...

</rich:column>

...

<f:facet name="footer">

<rich:datascroller id="ds"></rich:datascroller>

</f:facet>

</rich:dataTable>

Page 5: 72134099 RichFaces Trabajando Con Tablas

- Mediante el atributo for: almacena el id de la tabla sobre la que se está actuando.

<rich:dataTable id="tablaEjemplo" ...>

<rich:column>

...

</rich:column>

...

</rich:dataTable>

<rich:datascroller id="ds" for="tablaEjemplo" ...></rich:datascroller>

En ambos casos se consigue algo de este estilo:

El componente dataScroller genera una barra numerada que permite recorrer todos los elementos de la tabla.

Ordenando y filtrando tablas

La ordenación de tablas se realiza trabajando con el atributo sortBy asociado a los componentes <rich:column>. Se

muestra a continuación:

<rich:dataTable value="#{mbean1.facturas}" var="f" ...>

<rich:column sortBy="#{f.concepto}">

<f:facet name="header">

<h:outputText value="Concepto" />

</f:facet>

<h:outputText value="#{f.concepto}" />

</rich:column>

<rich:column>

<f:facet name="header">

<h:outputText value="Código de la factura" />

</f:facet>

Page 6: 72134099 RichFaces Trabajando Con Tablas

<h:outputText value="#{f.codigo}" />

</rich:column>

<rich:column>

<f:facet name="header">

<h:outputText value="Importe" />

</f:facet>

<h:outputText value="#{f.importe}" />

</rich:column> ...

</rich:dataTable>

El proceso de filtrado se lleva a cabo aplicando sobre las columnas, es decir, los componentes <rich:column> el atributo

filterBy.

<rich:dataTable value="#{mbean1.facturas}" var="f" ...>

<rich:column sortBy="#{f.concepto}" filterBy="#{f.concepto}">

<f:facet name="header">

<h:outputText value="Concepto" />

</f:facet>

<h:outputText value="#{f.concepto}" />

</rich:column>

<rich:column>

<f:facet name="header">

<h:outputText value="Código de la factura" />

</f:facet>

<h:outputText value="#{f.codigo}" />

</rich:column>

<rich:column>

<f:facet name="header">

<h:outputText value="Importe" />

</f:facet>

<h:outputText value="#{f.importe}" />

</rich:column> ...

</rich:dataTable>

...

Page 7: 72134099 RichFaces Trabajando Con Tablas

Si se aplican ambos conceptos sobre la misma tabla, se obtiene algo así:

Conclusiones

Los componentes para el manejo de tablas de RichFaces, utilizados de forma estándar, nos permiten mejorar las

prestaciones de nuestras tablas en cuanto al número de elementos renderizados en nuestras páginas simultáneamente,

pero en ningún caso, nos permite reducir el número de objetos almacenados en memoria ni el tipo de consultas que

realizo sobre la base de datos. En próximos artítulos se describe este proceso de mejora.

Page 8: 72134099 RichFaces Trabajando Con Tablas

RichFaces, trabajando con tablas (2)

Introducción

Se quiere trabajar con tablas paginadas que NO tengan que guardar en memoria TODOS los objetos de la tabla, estén siendo mostrados en pantalla o no. De esta forma se optimiza la memoria necesaria para la aplicación.

El modelo presentado en el artículo "RichFaces, trabajando con tablas (1)" maneja un conjunto de datos mediante una

lista Java (se modela mediante la interface List). De tal forma que, independiente de la paginación que se esté aplicando, todos los objetos de la tabla se encuentran cargados en memoria. Esto, en muchas ocasiones, no es viable.

La solución que se plantea en este artículo permite almacenar en memoria SÓLO los objetos que se están utilizando en

cada momento. Como ya se ha comentado anteriormente, no se trabaja con una implementación de la interface List. El modelo va a evolucionar para trabajar de una forma más sofisticada:

<rich:dataTable value="#{modeloImpl}"...>

...

</rich:dataTable>

El modelo será una clase que hereda de SerializableDataModel. Se modifica, a continuación, el ejemplo inicial descrito

en "RichFaces, trabajando con tablas (1)" para que no tengan que cargarse todos los objetos generados como resultado de la consulta en memoria.

Además, vamos a hacer que el MBean utilizado para esta página NO tenga que encontrarse en el alcance de sesión:

...

public class MBean2 {

private DataModel facturas;

...

Page 9: 72134099 RichFaces Trabajando Con Tablas

public DataModel getFacturas() {

return facturas;

}

public void setFacturas(DataModel facturas) {

this.facturas = facturas;

}

}

Pasamos a inyectar el modelo mediante el fichero de configuración de JavaServer Faces:

...

<managed-bean>

<managed-bean-name>paginacion2</managed-bean-name>

<managed-bean-class>

es.ematiz.paginacion.MBean2

</managed-bean-class>

<managed-bean-scope>request</managed-bean-scope>

<managed-property>

<property-name>facturas</property-name>

<value>#{modeloFacturas}</value>

</managed-property>

</managed-bean>

...

Nota: el MBean asociado al modelo de datos llamado modeloFacturas será definido posteriormente.

Es importante NO olvidar que vamos a tener que implementar una clase que herede de SerializableDataModel. Para entender de forma adecuada cómo hacerlo, se describen a continuación las clases que se van a manejar:

La clase DataModel: clase perteneciente a la especificación JavaServer Faces que permite definir modelos de datos

avanzados.

La clase ExtendedDataModel: es una clase abstracta que define modelos de datos avanzados.

La clase SerializableDataModel: el modelo de datos será una clase que hereda de ésta.

La clase DataModel

Esta clase es una abstracción de datos que puede utilizarse como fuente de datos para distintos componentes JSF que

soportan procesado de filas de sus componentes hijos.

La colección de datos subyacente de una instancia de DataModel se modela como una colección de objetos fila a la que se puede acceder mediante un índice.

Page 10: 72134099 RichFaces Trabajando Con Tablas

Métodos:

getRowCount(): número de filas de objetos representados por la instancia DataModel.

getRowData(): devuelve el objeto representado por la fila seleccionada por el índice de filas.

getRowIndex(): devuelve el índice de filas.

...

Se recomienda analizar de forma detallada la clase mediante documentación oficial.

La clase ExtendedDataModel

Es una extensión abstracta de DataModel definida por RichFaces para manejar estructuras de datos complejas.

- La clave o identificador para cada uno de los objetos no tiene que ser un índice entero. Puede ser cualquier tipo de objeto. Para modelar esta idea se deben implementar los métodos siguientes adaptándolos a nuestras necesidades:

public abstract void setRowKey(java.lang.Object key)

public abstract java.lang.Object getRowKey()

- El método walk() será el responsable de generar los objetos que va a manejar el componente JSF en la renderización.

public abstract void walk(

FacesContext context,

Page 11: 72134099 RichFaces Trabajando Con Tablas

DataVisitor visitor,

Range range,

Object argument)

throws java.io.IOException

Este método itera sobre el modelo mediante el patrón visitor para un rango dado.

Cada vez que el ciclo de vida JSF necesita recuperar el modelo de datos ejecuta este método, es decir, en una situación normal se ejecuta tres veces: fase de aplicación de la petición, fase de validación y fase de actualización del modelo.

- El método getSerializableModel(): un componente de iteración puede soportar operaciones de guardado de datos en las

fases de decodificación/validación y actualización para evitar llamadas innecesarias al modelo original. Por ejemplo, para

evitar peticiones a la base de datos hasta que todos los datos están validados.

Este método se ejecuta una vez en el ciclo de vida estándar JSF, en la fase de renderización.

La clase SerializableDataModel

Hereda de ExtendedDataModel añadiendo un nuevo método update() que sólo se ejecuta cuando se lanza una acción dentro del componente JSF asociado en la fase de actualización del modelo:

Diseño del modelo de datos

Una vez descritas las tres clases importantes que nos permitirán crear el modelo de datos, pasamos a construirlo para nuestro ejemplo de Facturas.

Como ya se ha comentado el modelo de datos hereda de la clase SerializableDataModel:

...

public class FacturaDataModel extends SerializableDataModel{

// Atributos del modelo de datos

private static final long serialVersionUID = 1L;

Page 12: 72134099 RichFaces Trabajando Con Tablas

// Clave o identificador de cada factura.

private Integer currentPk;

// Mapa que almacena id/Factura para los objetos que están manejando

// la tabla en este momento.

private Map<Integer,Factura> wrappedData =

new HashMap<Integer,Factura>();

// Lista de los identificadores que está manejando la tabla

// en cada caso.

private List<Integer> wrappedKeys = null;

private FacturaDataProvider dataProvider;

private boolean detached = false;

...

- El atributo currentPk representa a la clave primaria del objeto asociado a la tabla que se está manejando.

Para el caso de Factura, la clave es de tipo Integer, es por eso que currentPK es de tipo Integer. Si la clave primaria del

objeto manejado por la tabla fuera de otro tipo, deberíamos cambiar también el tipo asociado a este atributo. Por ejemplo, si fuera una cadena de caracteres sería:

private String currentPk;

- El atributo wrappedData es un mapa que debe almacenar pares de datos: identificador único del objeto y los objetos que

maneja la tabla en este momento. - El atributo wrappedKeys almacena los identificadores únicos de cada uno de los objetos que maneja la tabla.

private Map<Integer,Factura> wrappedData =

new HashMap<Integer,Factura>();

private List<Integer> wrappedKeys = null;

- El atributo detached es un booleano que vamos a utilizar para indicar cuándo hay que recuperar el modelo de datos de

la base de datos y cuando NO se debe hacer. Este atributo nos permite diferenciar entre peticiones que renderizan la tabla

paginada y peticiones POSTBACK. MUY IMPORTANTE a la hora de optimizar el acceso a la base de datos.

- El atributo dataProvider es el objeto Java que realmente se responsabiliza del acceso a base de datos. Posteriormente, se describe el proceso para crear esta clase.

El diseño del modelo de datos implica la implementación los métodos anteriormente comentados:

Método walk.

Método getSerializableModel.

Método update.

Método getRowCount.

Método getRowData.

Page 13: 72134099 RichFaces Trabajando Con Tablas

El método walk()

public void walk(FacesContext context, DataVisitor visitor,

Range range,

Object argument) throws IOException {

// Paso 1: Se recupera el rango de datos con el que trabaja la tabla

// paginada.

int firstRow = ((SequenceRange)range).getFirstRow();

int numberOfRows = ((SequenceRange)range).getRows();

// Se comprueba si el modelo esta serializado

if (detached) {

// Paso 2.1: Si el modelo está serializado NO se accede de nuevo

// a la base de datos.

for (Integer key:wrappedKeys) {

setRowKey(key);

visitor.process(context, key, argument);

}

} else {

// Paso 2.2: Si el modelo no esta serializado hay que volver a

// recuperarlo.

wrappedKeys = new ArrayList<Integer>();

// Para recuperar el modelo se actua sobre el proveedor

// de datos, dataProvider.

for (Factura f:dataProvider.getItemsByRange(firstRow,

numberOfRows)) {

wrappedKeys.add(f.getCodfactura());

wrappedData.put(f.getCodfactura(), f);

visitor.process(context, f.getCodfactura(),

argument);

}

}

}

Page 14: 72134099 RichFaces Trabajando Con Tablas

El método getSerializableModel()

public SerializableDataModel getSerializableModel(Range range) {

if (wrappedKeys!=null) {

detached = true;

return this;

} else {

return null;

}

}

El método update()

Este método se ejecuta, dentro del ciclo de vida JSF en la fase de actualización del modelo, como resultado de una acción

dentro de la tabla que, evidentemente, tiene que enviar la tabla como parte de la información a procesar en el servidor. Inicialmente este método puede estar vacío:

public void update() {

}

El método getRowData()

Devuelve el objeto asociado al identificador actual.

public Object getRowData() {

if (currentPk==null) {

return null;

} else {

Factura ret = wrappedData.get(currentPk);

if (ret==null) {

// Se utiliza el proveedor de datos para recuperar el objeto

// asociado a una determinada clave o identificador.

ret = getDataProvider().getItemByKey(currentPk);

wrappedData.put(currentPk, ret);

return ret;

Page 15: 72134099 RichFaces Trabajando Con Tablas

} else {

return ret;

}

}

}

El método getRowCount()

Devuelve el número de filas total de la información con la que se trabaja.

private Integer rowCount;

@Override

public int getRowCount() {

if (rowCount==null) {

rowCount = new Integer(getDataProvider().getRowCount());

return rowCount.intValue();

} else {

return rowCount.intValue();

}

}

El modelo de datos se define en el alcance request.

<managed-bean>

<managed-bean-name>modeloFacturas</managed-bean-name>

<managed-bean-class>

es.ematiz.paginacion.modelo.FacturaDataModel

</managed-bean-class>

<managed-bean-scope>request</managed-bean-scope>

<managed-property>

<property-name>dataProvider</property-name>

<value>#{proveedorFacturas}</value>

</managed-property>

</managed-bean>

El proveedor de datos va a ser definido como MBean y, como se puede ver en el código, será inyectado en el modelo de

datos como una propiedad manejada. Se describe a continuación el proceso de creación de dicho proveedor.

Page 16: 72134099 RichFaces Trabajando Con Tablas

El proveedor de datos

Es el objeto responsable de la interacción con el sistema de almacenamiento sobre el que trabajamos para recuperar la

información. Esta clase debe implementar la interface DataProvider<T> que se encuentra en el paquete

org.richfaces.model. Esta especificación define los siguientes métodos:

getItemByKey: devuelve un objeto a partir de su clave.

getItemsByRange: devuelve una lista de objetos en función de un rango de valores definido como parámetro.

getKey: devuelve la clave asociada a un objeto pasado como parámetro.

getRowCount: número de filas total.

Siguiendo nuestro ejemplo, el proveedor de datos para Facturas sería algo tal que así:

...

public class FacturaDataProvider

implements DataProvider<Factura>{

// Atributos de la clase

private static final long serialVersionUID = 1L;

private Integer size = null;

private List<Factura> rango;

private Map<Integer,Factura> mapa =

new HashMap<Integer,Factura>();

...

}

- El atributo size define el número de elementos que se manejan.

- El atributo rango define el conjunto de objetos que realmente se van a cargar en memoria. - El atributo mapa almacena pares Identificador de objeto, objeto.

El método getItemByKey()

Como su nombre indica, este método debe devolver el objeto asociado a una determinada clave o identificador.

@Override

public Factura getItemByKey(Object key) {

// Uno de los atributos de la clase es una mapa que almacena pares

// claves/Objetos, por lo tanto, teniendo la clave recuperar el

objeto

// es algo evidente.

return mapa.get(key);

}

Page 17: 72134099 RichFaces Trabajando Con Tablas

El método getKey()

Este método recupera la clave asociada al objeto pasado como parámetro.

@Override

public Object getKey(Factura f) {

// Para el caso de la clase Factura, su clave es codigo.

return f.getCodigo();

}

El método getItemsByRange()

Es el método más importante, siendo responsable de la recuperación de SÓLO los objetos que va a mostrar la tabla en

cada momento, es decir SÓLO recupera el rango definido mediante los parámetros firstRow y endRow.

public List<Factura> getItemsByRange(int firstRow, int endRow) {

FacturaBD servicio = new FacturaBD();

rango = servicio.findFacturas(firstRow, endRow);

Iterator<Factura> it = rango.iterator();

while (it.hasNext()) {

Factura factura = (Factura) it.next();

mapa.put(factura.getCodfactura(), factura);

}

return rango;

}

El método getRowCount()

Este método devuelve como valor de retorno el número total de datos con los que trabaja la tabla. Este valor determina el

tamaño del scroller asociado a la tabla.

@Override

public int getRowCount() {

if(size==null){

FacturaBD servicio = new FacturaBD();

Page 18: 72134099 RichFaces Trabajando Con Tablas

size = servicio.getNumFacturas();

}

return size.intValue();

}

Declaración del proveedor en faces-config.xml

El proveedor de datos debe ser un objeto definido en el alcance sesión, vamos a aprovechar el fichero de configuración de

JavaServer Faces para definirlo y posteriormente inyectarlo en el modelo de datos:

<managed-bean>

<managed-bean-name>proveedorFacturas</managed-bean-name>

<managed-bean-class>

es.ematiz.paginacion.modelo.FacturaDataProvider

</managed-bean-class>

<managed-bean-scope>session</managed-bean-scope>

</managed-bean>

Como ya se ha comentado anteriormente, el proveedor debe ser inyectado en el modelo de datos. Se muestra de nuevo la estructura que debemos incluir en el fichero de configuración de JavaServer Faces.

<managed-bean>

<managed-bean-name>modeloFacturas</managed-bean-name>

<managed-bean-class>

es.ematiz.paginacion.modelo.FacturaDataModel

</managed-bean-class>

<managed-bean-scope>request</managed-bean-scope>

<managed-property>

<property-name>dataProvider</property-name>

<value>#{proveedorFacturas}</value>

</managed-property>

</managed-bean>

Incorporación en la vista

El modelo de datos definido y su proveedor asociado han sido definidos aprovechando la inyección de dependencias de

JSF. Para poder utilizarlos en la vista sólo debemos utilizar lenguaje de expresión:

Page 19: 72134099 RichFaces Trabajando Con Tablas

<h:form>

<rich:dataTable id="tablaFacturas"

value="#{paginacion2.facturas}"

var="f" rows="5" reRender="scroll01"

width="100%">

<rich:column id="concepto">

<f:facet name="header">

<h:outputText value="Concepto"/>

</f:facet>

<h:outputText value="#{f.concepto}" />

</rich:column>

...

</rich:dataTable>

<rich:datascroller id="scroll01" for="tablaFacturas"

maxPages="5" />

</h:form>

A continuación se muestra el resultado:

No se cargan en memoria todos los objetos, SÓLO los que se están visualizando en la tabla.

Análisis del ciclo de vida

La primera vez que se quiere visualizar la página, el ciclo de vida (como ya hemos comentado anteriormente es más

corto):

Fase de restauración de la vista.

Fase de renderización: en esta fase se ejecutan los métodos del modelo de datos anteriormente descritos. En el caso

del método walk(), como la variable detached está a falso, obligatoriamente accede a base de datos y recupera SÓLO

los objetos que va a mostrar la tabla. El número de objetos cargados en memoria es mucho menor que en el primero

caso. Posteriormente, se ejecuta el método getSerializableModel() que devuelve el modelo serializado poniendo la

variable detached a verdadero (esto provoca que cuando se hagan peticiones sobre la página -Postbacks- no se recupere el modelo de la base de datos, ya que se encuentra serializado).

Page 20: 72134099 RichFaces Trabajando Con Tablas

Finalmente, se obtiene el resultado de la renderización:

Para este caso, las únicas acciones que vamos a poder generar sobre la tabla se realizan mediante el scroller.

Éste va a lanzar una única petición a base de datos en la fase de renderización. En las demás fases no se hace nada

relacionado con el acceso a base de datos, por lo tanto, todo correcto.

Page 21: 72134099 RichFaces Trabajando Con Tablas

RichFaces, trabajando con tablas (3): ordenación y filtrado manuales

Introducción

El uso de los atributos sortBy y filterBy del componente rich:column NO será válido cuando se trabaja con modelos de

datos complejos como el que se presentó en el artículo "RichFaces, trabajando con tablas (2)". Se deben implementar mecanismos de ordenación y filtrado manuales que sean capaces de manejar este modelo paginado.

Se van a definir commandLinks dentro de las columnas de la tabla que van a lanzar peticiones Ajax para modificar el orden en el que se muestran los datos dentro del dataTable.

Se van a crear inputText para definir mecanismos de filtrado.

Ordenación manual

Modificaciones en la vista

Para aplicar ordenación sobre una estructura de datos compleja se debe modificar la renderización. Se incorpora una

nueva acción dentro de la faceta de cabecera que pasa como parámetro, en la petición que genera, el campo a partir del cuál se va a ordenar.

Por ejemplo, si se hace click sobre la columna Código de factura se enviará un parámetro llamado sortField cuyo valor será codfactura y cuando se genere la respuesta se re-renderizará la tabla paginada:

<h:column>

<f:facet name="header">

<a4j:commandLink value="Código de factura" reRender="tabla">

<a4j:actionparam name="sortField" value="codfactura" />

</a4j:commandLink>

</f:facet>

<h:outputText value="#{f.codfactura}"></h:outputText>

</h:column>

¿En este caso se deben utilizar atributos para optimizar el envío de información? ¿ajaxSingle = “true”? No, la petición

desencadenada por esta acción debe enviar la información de la tabla para poder procesarla mediante el ciclo de vida

estándar.

¿Algún tipo de renderización adicional? Queremos que, una vez que el orden de los datos sea modificado, sólo sea re-

renderizada la tabla. Los demás contenidos de la página deben quedar intactos.

Por esta razón, se utiliza en atributo reRender asociado a la tabla.

<rich:dataTable id="tablaFacturas" value="#{paginacion2.facturas}"

Page 22: 72134099 RichFaces Trabajando Con Tablas

var="f" rows="5" reRender="scroll01" width="100%">

<rich:column id="concepto">

<f:facet name="header">

<h:outputText value="Concepto"></h:outputText>

</f:facet>

<a4j:commandLink id="accion" ajaxSingle="true" limitToList="true">

<h:outputText value="#{f.concepto}" />

</a4j:commandLink>

</rich:column>

<rich:column>

<f:facet name="header">

<a4j:commandLink value="Código de factura"

reRender="tablaFacturas">

<a4j:actionparam name="sortField" value="codfactura" /> </a4j:commandLink>

</f:facet>

<h:outputText value="#{f.codfactura}"></h:outputText>

</rich:column>

El método update()

El modelo de datos propuesto en el artículo "RichFaces, trabajando con tablas (2)" incorpora un método llamado update()

como responsable de todos los procesos de actualización del modelo. Este método se ejecuta, dentro del ciclo de vida JSF

en la fase de actualización del modelo, como resultado de una acción dentro de la tabla que, evidentemente, tiene que

enviar la tabla como parte de la información a procesar en el servidor. El caso que estamos analizando cumple todas esas condiciones. Por esta razón, este método nos va a permitir implementar el concepto de ordenación.

...

public void update() {

// Codigo para cambiar el criterio de ordenación.

}

...

Se muestra a continuación código de ejemplo:

@Override

public void update() { // Paso 1: Se comprueba si se aplica criterio

de ordenación.

if(getCriterioOrdenacion()!=null){

// Paso 2: Se recupera al criterio de ordenación a aplicar.

final String nuevoCriterio = getCriterioOrdenacion().toString();

// Paso 3: Se comprueba si el criterio de ordenación es igual al

que está activo.

if(nuevoCriterio.equals(dataProvider.getOrdenacion())){

Page 23: 72134099 RichFaces Trabajando Con Tablas

// Paso 4: Si el criterio de ordenación es el mismo, debemos

cambiar

// el orden.

dataProvider.setDescendente(!dataProvider.isDescendente());

}

dataProvider.setOrdenacion(nuevoCriterio);

}

// El atributo detached indica si el modelo de datos está serializado

o si hay

// que volver a la base de datos a recuperar la información de nuevo.

// En el caso de un cambio de ordenación, debemos lanzar una nueva

consulta.

detached = false;

}

Como se ha podido ver en el código, se crea y utiliza un método auxiliar llamado getCriterioOrdenacion para manejar el

criterio de ordenación:

protected Object getCriterioOrdenacion(){

// Paso 1: Se recupera el contexto JSF

final FacesContext contexto = FacesContext.getCurrentInstance();

// Paso 2: Se obtiene el parametro pasado en la petición.

final Object o =

contexto.getExternalContext().getRequestParameterMap().get("sortField");

return o;

}

El método getItemsByRange del proveedor

Lógicamente, este método debe ser modificado para manejar el concepto de ordenación. Por ejemplo

servicioContabilidad.findAllPaginandoOrdenando(inicio, tam, asc,

propiedad);

Análisis del ciclo de vida

La primera vez que se quiere visualizar la página, el ciclo de vida (como ya hemos comentado anteriormente es más

corto):

Page 24: 72134099 RichFaces Trabajando Con Tablas

Fase de restauración de la vista.

Fase de renderización:

En esta fase se ejecutan los métodos del modelo de datos anteriormente descritos. En el caso del método walk(),

como la variable detached está a falso, obligatoriamente accede a base de datos y recupera SÓLO los objetos que

va a mostrar la tabla. El número de objetos cargados en memoria es mucho menor que en el primero caso.

Posteriormente, se ejecuta el método getSerializableModel() que devuelve el modelo serializado poniendo la

variable detached a verdadero (esto provoca que cuando se hagan peticiones sobre la página -Postbacks- no se recupere el modelo de la base de datos, ya que se encuentra serializado).

Una vez que la página se renderiza, se muestra:

Al hacer click sobre el enlace Código de factura se genera una petición al servidor. En ese caso se desencadena un ciclo de vida completo:

La petición es de tipo POSTBACK, es decir, el modelo está serializado y la variable detached está a true, por lo

tanto, aunque se ejecute el método walk() del modelo NO se lanzan consultas a la base de datos.

Cuando se llega a la fase de actualización se ejecuta el método update(). Éste cambia el criterio de ordenación y

pone la variable detached a falso.

En la fase de renderización, como detached está a falso, cuando se ejecuta el método walk() SÍ se accede a la base

de datos aplicando el criterio de ordenación seleccionado. Como en la vista el atributo reRender está asociado a la tabla, ésta vuelve a renderizarse con los nuevos valores.

Filtrado manual

Conceptualmente, este proceso es muy parecido al de ordenación. Se debe modificar la vista para incorporar un campo

que actúe como filtro y, posteriormente, se debe modificar el modelo de datos que proporciona registros a la tabla para que cuando se active el filtro actúe en consecuencia.

Modificaciones en la vista

Se incorpora un campo de texto que genera peticiones AJAX para activar el proceso de filtrado:

...

<rich:column id="concepto">

<f:facet name="header">

<h:outputLabel value="Concepto" for="filtroConcepto"></h:outputLabel>

Page 25: 72134099 RichFaces Trabajando Con Tablas

<h:inputText id="filtroConcepto"

value="#{paginacion2.facturas.dataProvider.filtroConcepto}"

style="width:90px" >

<a4j:support event="onblur" reRender="tablaFacturas,scroll01"

ajaxSingle="true" limitToList="true">

</a4j:support>

</h:inputText>

</f:facet>

<h:outputText value="#{f.concepto}" />

</rich:column>

...

Modificaciones en el método getItemsByRange()

Se debe modificar este método para que sea capaz de manejar filtros:

...

rango = servicioContabilidad.

findAllPaginandoOrdenandoFiltrado(

firstRow,

endRow,

!descendente,

ordenacion,

filtroConcepto);

...

El resultado de estas modificaciones generan la siguiente vista:

Page 26: 72134099 RichFaces Trabajando Con Tablas

Si se escribe en el filtro se genera un filtrado: