Table of Contents

Capítulo 5: Datos tabulares
Propiedades iniciales y resaltar filas
Filtros y condición base
Select íntegro
Orden por defecto

Capítulo 5: Datos tabulares

Datos tabulares son aquellos que se visualizan en formato de tabla. Cuando creamos un módulo de OpenXava convencional el usuario puede gestionar la información sobre ese componente con una lista como ésta:
tab_es010.jpg
Esta lista permite al usuario:
La lista por defecto suele ir bien, y además el usuario puede personalizarsela. Sin embargo, a veces conviene modificar el comportamiento de la lista. Esto se hace mediante la anotación @Tab dentro de la definición de la entidad.
La sintaxis de @Tab es:
@Tab(
    name="nombre",                    // 1
    filter=clase del filtro,          // 2
    rowStyles=array de @RowStyle,     // 3
    properties="propiedades",         // 4
    baseCondition="condición base",   // 5
    defaultOrder="orden por defecto"  // 6
)
public class MyEntity {
  1. name (opcional): Podemos definir varios tabs para una entidad (mediante la anotación @Tabs), y ponerle un nombre a cada uno. Este nombre se usará después para indicar que tab queremos usar (normalmente en aplicación.xml al definir un módulo).
  2. filter (opcional): Permite definir programáticamente un filtro a realizar sobre los valores que introduce el usuario cuando quiere filtrar.
  3. rowStyles (varios, opcional): Una forma sencilla de especificar una estilo de visualización diferente para ciertas filas. Normalmente para resaltar filas que cumplen cierta condición. Especificamos un array de @RowStyle, así podemos usar varios estilo por tab.
  4. properties (opcional): La lista de propiedades a visualizar inicialmente. Pueden ser calificadas.
  5. baseCondition (opcional): Es una condición que aplicará siempre a los datos visualizados añadiendose a las que pueda poner el usuario.
  6. defaultOrder (opcional): Para especificar el orden en que aparece los datos en la lista inicialmente.

Propiedades iniciales y resaltar filas

La personalización más simple es indicar las propiedades a visualizar inicialmente:
@Tab(
    rowStyles=@RowStyle(style="highlight", property="tipo", value="fijo"),
    properties="nombre, tipo, comercial.nombre, direccion.municipio," +
        "comercial.nivel.descripcion, direccion.estado.nombre"
)
Vemos como podemos poner propiedades calificadas (que pertenecen a referencias) hasta cualquier nivel. Estas serán las propiedades que salen la primera vez que se ejecuta el módulo, después cada usuario puede escoger cambiar las propiedades que quiere ver.
En este caso vemos también como se indica un @RowStyle; estamos diciendo que aquellos objetos cuya propiedad tipo tenga el valor fijo han de usar el estilo highlight. El estilo ha de definirse en la hoja de estilos CSS. El estilo highlight ya viene predefinido con OpenXava, pero se pueden añadir más. El resultado visual del anterior tab es:
tab_es020.jpg

Filtros y condición base

Una técnica habitual es combinar un filtro con una condición base:
@Tab(name="Actuales",
    filter=FiltroAñoActual.class,
    properties="año, numero, sumaImportes, iva, cantidadLineas, pagada, cliente.nombre",
    baseCondition="${año} = ?"
)
 
La condición tiene la sintaxis SQL, ponemos ? para los argumentos y los nombres de propiedades entre ${}. En este caso usamos el filtro para dar valor al argumento. El código del filtro es:
package org.openxava.test.filtros;
 
import java.util.*;
 
import org.openxava.filters.*;
 
/**
 * @author Javier Paniza
 */
 
public class FiltroAñoActual implements IFilter {            // (1)
 
    public Object filter(Object o) throws FilterException {  // (2)
        Calendar cal = Calendar.getInstance();
        cal.setTime(new java.util.Date());
        Integer año = new Integer(cal.get(Calendar.YEAR));
        Object [] r = null;
        if (o == null) {                                    // (3)
            r = new Object[1];
            r[0] = año;
        }
        else if (o instanceof Object []) {                  // (4)
            Object [] a = (Object []) o;
            r = new Object[a.length + 1];
            r[0] = año;
            for (int i = 0; i < a.length; i++) {
                r[i+1]=a[i];
            }
        }
        else {                                              // (5)
            r = new Object[2];
            r[0] = año;
            r[1] = o;
        }
 
        return r;
    }
 
}
Un filtro recoge los argumentos que el usuario teclea para filtrar la lista y los procesa devolviendo lo que al final se envía a OpenXava para que haga la consulta. Como se ve ha de implementar IFilter (1) lo que lo obliga a tener un método llamado filter (2) que recibe un objeto que el valor de los argumentos y devuelve los argumentos que al final serán usados. Estos argumentos pueden ser nulo (3), si el usuario no ha metidos valores, un objeto simple (5), si el usuario a introducido solo un valor o un array de objetos (4), si el usuario a introducidos varios valores. El filtro ha de contemplar bien todos los casos. En el ejemplo lo que hacemos es añadir delante el año actual, y así se usa como argumento a la condición que hemos puesto en nuestro tab.
Resumiendo el tab que vemos arriba solo sacará las facturas correspondientes al año actual.
Podemos ver otro caso:
@Tab(name="AñoDefecto",
    filter=FiltroAñoDefecto.class,
    properties="año, numero, cliente.numero, cliente.nombre, sumaImportes, " +
        "iva, cantidadLineas, pagada, importancia",
    baseCondition="${año} = ?"
)
 
En este caso el filtro es:
package org.openxava.test.filtros;
 
import java.util.*;
 
import org.openxava.filters.*;
 
/**
 * @author Javier Paniza
 */
 
public class FiltroAñoDefecto extends BaseContextFilter {    // (1)
 
    public Object filter(Object o) throws FilterException {
        if (o == null) {
            return new Object [] { getAñoDefecto() };        // (2)
        }
        if (o instanceof Object []) {
            List c = new ArrayList(Arrays.asList((Object []) o));
            c.add(0, getAñoDefecto());                       // (2)
            return c.toArray();
        }
        else {
            return new Object [] { getAñoDefecto(), o };     // (2)
        }
    }
 
    private Integer getAñoDefecto() throws FilterException {
        try {
            return getInteger("xavatest_añoDefecto");        // (3)
        }
        catch (Exception ex) {
            ex.printStackTrace();
            throw new FilterException(
            "Imposible obtener año defecto asociado a esta sesión");
        }
    }
 
}
Este filtro desciende de BaseContextFilter, esto le permite acceder al valor de los objetos de sesión de OpenXava. Vemos como usa un método getAñoDefecto() (2) que a su vez llama a getInteger() (3) el cual (al igual que getString(), getLong() o el más genérico get()) nos permite acceder al valor del objeto xavatest_añoDefecto. Esto objeto lo definimos en nuestro archivo controladores.xml de esta forma:
<objeto nombre="xavatest_añoDefecto" clase="java.lang.Integer" valor="1999"/>
Las acciones lo pueden modificar y tiene como vida la sesión del usuario y es privado para cada módulo. De esto se habla más profundamente en el capítulo 7.
Esto es una buena técnica para que en modo lista aparezcan unos datos u otros según el usuario o la configuración que éste haya escogido.
También es posible acceder a variables de entorno dentro de un filtro de tipo BaseContextFilter, usando el método getEnvironment(), de esta forma:
new Integer(getEnvironment().getValue("XAVATEST_AÑO_DEFECTO"));
Para aprender más sobre variable de entorno ver el capítulo 7 sobre controladores.

Select íntegro

Tenemos la opción de poner el select completo para obtener los datos del tab:
@Tab(name="SelectIntegro",
    properties="codigo, descripcion, familia",
    baseCondition=
        "select" +
        "    ${codigo}, ${descripcion}, XAVATEST@[email protected] " +
        "from " +
        "    XAVATEST@separator@SUBFAMILIA, XAVATEST@separator@FAMILIA " +
        "where " +
        "    XAVATEST@[email protected] = " +
        "    XAVATEST@[email protected]"
)
 
Esto es mejor usarlo solo en casos de extrema necesidad. No suele ser necesario, y al usarlo el usuario no podrá personalizarse la vista.

Orden por defecto

Por último, establecer un orden por defecto es harto sencillo:
@Tab(name="Simple", properties="año, numero, fecha",
    defaultOrder="${año} desc, ${numero} desc"
}
 
Este orden es solo el inicial, el usuario puede escoger otro con solo pulsar la cabecera de una columna.