Capítulo 4: Vista
OpenXava genera a partir del modelo una interfaz gráfica de usuario por defecto. Para muchos casos sencillos esto es suficiente, pero muchas veces es necesario modelar con más precisión la forma de la interfaz de usuario o vista. En este capítulo vamos a ver cómo.
Disposición
La anotación
@View se puede usar en una entidad o una clase incrustable para definir la disposición de sus miembros en la interfaz de usuario.
La sintaxis para definir una vista (
@View) es:
@View(
name="nombre", // 1
members="miembros" // 2
)
public class MiEntidad {
- name (opcional): El nombre identifica a la vista, y puede ser usado desde otro lugares de OpenXava (por ejemplo desde aplicacion.xml) o desde otra entidad. Si no se pone nombre se asume que es la vista por defecto, es decir la forma normal de visualizar el objeto.
- members (opcional): Indica los miembros que tienen que salir y como tienen que estar dispuestos en la interfaz gráfica. Por defecto visualiza todos los miembros no ocultos en el orden en que están declarados en el modelo. Dentro de miembros podemos usar los elementos seccion y grupo para indicar la disposición; o el elemento accion para mostrar un vínculo asociado a una acción propia dentro de la vista.
Podemos definir varias vistas para una entidad usando la anotación
@Views.
Por defecto (es decir si no definimos ni siquiera el elemento
@View en nuestra clase) se visualizan todos los miembros del objeto en el orden en que están en el modelo, y se disponen uno debajo del otro.
Por ejemplo, un modelo así:
@Entity
@IdClass(OficinistaKey.class)
public class Oficinista {
@Id @Required
@Column(length=3, name="ZONA")
private int codigoZona;
@Id @Required
@Column(length=3, name="OFICINA")
private int codigoOficina;
@Id @Required
@Column(length=3, name="CODIGO")
private int codigo;
@Required @Column(length=40)
private String nombre;
// Getters and setters
...
}
Generaría una vista con este aspecto:
Podemos escoger que miembros queremos que aparezcan y en que orden, con el atributo
members:
@Entity
@IdClass(OficinistaKey.class)
@View(members="codigoZona; codigoOficina; codigo")
public class Oficinista {
En este caso ya no aparece el
nombre en la vista.
También se puede usar
members para refinar la disposición:
@View(members=
"codigoZona, codigoOficina, codigo;" +
"nombre"
)
Podemos observar como separamos los nombres de miembros con comas y punto y comas, esto nos sirve para indicar la disposición, con la coma el miembro se pone a continuación, y con punto y coma en la línea siguiente, esto es la vista anterior quedaría así:
Grupos
Con los grupos podemos agrupar un conjunto de propiedades relacionadas, y esto tiene un efecto visual. Para definir un grupo solo necesitamos poner el nombre del grupo y después sus miembros entre corchetes. Justo de esta forma:
@View(members=
"id [ codigoZona, codigoOficina, codigo ];" +
"nombre"
)
En este caso el resultado sería:
Se puede observar como las tres propiedades puestas en el grupo aparecen dentro de un marquito, y como
nombre aparece fuera. El punto y coma antes de
nombre es para que aparezca abajo, si no aparecería a continuación.
Podemos poner varios grupos en una vista:
@View(members=
"general [" +
" codigo;" +
" tipo;" +
" nombre;" +
"]" +
"contacto [" +
" telefono;" +
" correoElectronico;" +
" sitioWeb;" +
"]"
)
En este caso se visualizaría así:
Si queremos que aparezca uno debajo del otro debemos poner un punto y coma después del grupo, como sigue:
@View(members=
"general [" +
" codigo;" +
" tipo;" +
" nombre;" +
"];" +
"contacto [" +
" telefono;" +
" correoElectronico;" +
" sitioWeb;" +
"]"
)
En este caso se visualizaría así:
Anidar grupos está soportado. Esta interesante característica permite disponer los elementos de la interfaz gráfica de una forma simple y flexible. Por ejemplo, si definimos una vista como ésta:
@View(members=
"factura;" +
"datosAlbaran [" +
" tipo, codigo;" +
" fecha;" +
" descripcion;" +
" envio;" +
" datosTransporte [" +
" distancia; vehiculo; modoTransporte; tipoConductor;" +
" ]" +
" datosEntregadoPor [" +
" entregadoPor;" +
" transportista;" +
" empleado;" +
" ]" +
"]"
)
Obtendremos lo siguiente:
A veces es útil distribuir los miembros alineándolos por columnas, como en una tabla. Por ejemplo, la siguiente vista:
@View(name="Amounts", members=
"año, numero;" +
"importes [" +
"descuentoCliente, descuentoTipoCliente, descuentoAño;" +
"sumaImportes, porcentajeIVA, iva;" +
"]"
)
...será visualizada como sigue:
Esto es feo. Sería mejor tener la información alineada por columnas. Podemos definir el grupo de esta forma:
@View(name="Amounts", members=
"año, numero;" +
"importes [#" +
"descuentoCliente, descuentoTipoCliente, descuentoAño;" +
"sumaImportes, porcentajeIVA, iva;" +
"]"
)
Notemos que usamos
[# en vez de
[. Ahora obtenemos este resultado:
Ahora, gracias al
#, los miembros están alineado por columnas.
Esta prestación esta disponible también para las secciones (ver abajo).
Secciones
Además de en grupo los miembros se pueden organizar en secciones. Para definir una sección solo necesitamos poner el nombre de la sección y después sus miembros entre llaves. Veamos un ejemplo en la entidad
Factura:
@View(members=
"año, numero, fecha, pagada;" +
"comentario;" +
"cliente { cliente }" +
"lineas { lineas }" +
"importes { sumaImportes; porcentajeIVA; iva }" +
"albaranes { albaranes }"
)
El resultado visual sería:
Las secciones se convierten en pestañitas que el usuario puede pulsar para ver la información contenida en esa sección. Podemos observar también como en la vista indicamos todo tipo de miembros (y no solo propiedades), así
cliente es una referencia,
lineas y
albaranes son colecciones.
Se permiten secciones anidadas. Por ejemplo, podemos definir una vista como ésta:
@View(name="SeccionesAnidadas", members=
"año, numero, fecha;" +
"cliente { cliente }" +
"datos {" +
" lineas { lineas }" +
" importes {" +
" iva { porcentajeIVA; iva }" +
" sumaImportes { sumaImportes }" +
" }" +
"}" +
"albaranes { albaranes }"
)
En este caso podemos obtener una interfaz gráfica como esta:
Al igual que en los grupos, las secciones permiten usar
# para conseguir alineado por columnas, así:
@View(name="ImportesAlineadosEnSeccion", members=
"año, numero;" +
"cliente { cliente }" +
"lineas { lineas }" +
"importes {#" +
"descuentoCliente, descuentoTipoCliente, descuentoAño;" +
"sumaImportes, porcentajeIVA, iva;" +
"}"
)
Con el mismo efecto que en el caso de los
grupos.
Filosofía para la disposición
Es de notar tenemos grupos y no marcos y secciones y no pestañas. Porque en las vistas de OpenXava intentamos mantener un nivel de abstracción alto, es decir, un grupo es un conjunto de propiedades relacionadas semánticamente, y las secciones nos permite dividir la información en partes cuando tenemos mucha y posiblemente no se pueda visualizar toda a la vez, el que los grupos se representen con marquitos y las secciones con pestañas es una cuestión de implementación, pero el generador del interfaz gráfico podría escoger usar un árbol u otro control gráfico para representar las secciones, por ejemplo.
Normas para las anotaciones de vista
Podemos anotar un miembro (propiedad, referencia o colección) con varias anotaciones que refinan su estilo de visualización y comportamiento. Además podemos definir que el efecto de estas anotaciones solo aplica a algunas vistas.
Por ejemplo, si tenemos una entidad como esta:
@Entity
@Views({
@View( members="codigo; tipo; nombre; direccion" ),
@View( name="A", members="codigo; tipo; nombre; direccion; comercial" ),
@View( name="B", members="codigo; tipo; nombre; comercial; comercialAlternativo" ),
@View( name="C", members="codigo; tipo; nombre; direccion; lugaresEntrega" )
})
public class Cliente {
Y queremos que la propiedad
nombre sea de solo lectura. Podemos anotarlo de esta manera:
@ReadOnly
private String nombre;
De esta forma
nombre es de solo lectura en todas las vistas. Ahora bien, puede que queramos que
nombre sea de solo lectura solo en las vistas B y C, entonces podemos definir el miembro como sigue:
@ReadOnly(forViews="B, C")
private String nombre;
Otra forma para definir este mismo caso es:
@ReadOnly(notForViews="DEFAULT, A")
private String nombre;
Usando
notForViews indicamos las vistas donde la propiedad
nombre es de solo lectura. DEFAULT se usa para referenciar a la vista por defecto, la vista sin nombre.
Algunas anotaciones tiene uno o más valores, por ejemplo para indicar que vista del tipo referenciado se usará para visualizar una referencia usamos la anotación
@ReferenceView:
@ReferenceView("Simple")
private Comercial comercial;
En este caso cuando se visualiza el comercial se usa la vista
Simple, definida en la clase
Comercial.
¿Qué ocurre si queremos usar la vista
Simple de
Comercial solo en la vista
B de
Cliente? Es fácil:
@ReferenceView(forViews="B", value="Simple")
private Comercial comercial;
¿Qué ocurre si lo que queremos es usar la vista
Simple de
Comercial solo en la vista
B de
Cliente y la vista
MuySimple de
Comercial para la vista
A de
Cliente? En este caso hemos de usar varias
@ReferenceView agrupandolas con
@ReferenceViews, justo así:
@ReferenceViews({
@ReferenceView(forViews="B", value="Simple"),
@ReferenceView(forViews="A", value="MuySimple")
})
Estas normas aplican a todas las anotaciones de este capítulo, excepto
@View y
@Views.
Personalización de propiedad
Podemos refinar la forma de visualización y comportamiento de una propiedad en la vista usando las siguientes anotaciones:
@ReadOnly // 1
@LabelFormat // 2
@DisplaySize // 3
@OnChange // 4
@Action // 5
@Editor // 6
private tipo nombrePropiedad;
Todas estas anotaciones siguen las
normas para anotaciones de vista y todas ellas son opcionales. OpenXava siempre asume valores por defecto correcto si se omiten.
- @ReadOnly (OX): Si marcas una propiedad con esta anotaciones no será nunca editable por el usuario en esta vista. Una alternativa a esto es hacer la propiedad editable/no editable programáticamente usando org.openxava.view.View.
- @LabelFormat (OX): Forma en que se visualiza la etiqueta para esta propiedad. Su valor puede ser LabelFormatType.NORMAL, LabelFormatType.SMALL o LabelFormatType.NO_LABEL.
- @DisplaySize (OX): La longitud en caracteres del editor en la interfaz de usuario usado para visualizar esta propiedad. El editor mostrará solo los caracteres indicados con longitud-visual pero permite que el usuario introduzca hasta el total de la longitud de la propiedad. Si @DisplaySize no se especifica se asume el valor de la longitud de la propiedad.
- @OnChange (OX): Acción a realizar cuando cambia el valor de esta propiedad. Solo una acción @OnChange por vista está permitida.
- @Action (OX): Acciones (mostradas como vínculos, botones o imágenes al usuario) asociadas (visualmente) a esta propiedad y que el usuario final puede ejecutar. Es posible definir varias @Action por cada vista.
- @Editor (OX): Nombre del editor a usar para visualizar la propiedad en esta vista. El editor tiene que estar declarado en OpenXava/xava/default-editors.xml o xava/editores.xml de nuestro proyecto.
Formato de etiqueta
Un ejemplo sencillo para cambiar el formato de la etiqueta (
@LabelFormat):
@LabelFormat(LabelFormatType.SMALL)
private int codigoPostal;
En este caso el código postal lo visualiza así:
El formato
LabelFormatType.NORMAL es el que hemos visto hasta ahora (con la etiqueta grande y la izquierda) y el formato
LabelFormatType.NO_LABEL simplemente hace que no salga etiqueta.
Evento de cambio de valor de propiedad
Si queremos reaccionar al evento de cambio de valor de una propiedad podemos user
@OnChange como sigue:
@OnChange(AlCambiarNombreCliente.class)
private String nombre;
El código que se ejecutará será:
package org.openxava.test.actions;
import org.openxava.actions.*;
import org.openxava.test.model.*;
/**
* @author Javier Paniza
*/
public class AlCambiarNombreCliente extends OnChangePropertyBaseAction { // 1
public void execute() throws Exception {
String valor = (String) getNewValue(); // 2
if (valor == null) return;
if (valor.startsWith("Javi")) {
getView().setValue("tipo", Cliente.Tipo.FIJO); // 3
}
}
}
La acción ha implementar
IOnChangePropertyAction aunque es más cómodo hacer que descienda de
OnChangePropertyBaseAction (1). Dentro de la acción tenemos disponible
getNewValue() (2) que proporciona el nuevo valor que ha introducido el usuario, y
getView() (3) que nos permite acceder programáticamente a la vista (
View) (cambiar valores, ocultar miembros, hacerlos editables, o lo que queramos).
Acciones de la propiedad
También podemos especificar acciones (
@Action) que el usuario puede pulsar directamente:
@Action("Albaran.generarNumero")
private int numero;
En este caso en vez de la clase de la acción se pone un identificador que consiste en el nombre de controlador y nombre de acción. Esta acción ha de estar registrada en
controladores.xml de la siguiente forma:
<controlador nombre="Albaran">
...
<accion nombre="generarNumero" oculta="true"
clase="org.openxava.test.acciones.GenerarNumeroAlbaran">
<usa-objeto nombre="xava_view"/>
</accion>
...
</controlador>
Las acciones se visualizan con un vínculo o imagen al lado del editor de la propiedad. Como sigue:
Por defecto el vínculo de la acción aparece solo cuando la propiedad es editable, ahora bien si la propiedad es de solo lectura (
@ReadOnly) o
calculada entonces está siempre disponible. Podemos usar el atributo
alwaysEnabled a
true para que el vínculo esté siempre presente, incluso si la propiedad no es editable. Como sigue:
@Action(value="Albaran.generarNumero", alwaysEnabled=true)
El atributo
alwaysEnabled es opcional y su valor por defecto es
false.
El código de la acción anterior es:
package org.openxava.test.acciones;
import org.openxava.actions.*;
/**
* @author Javier Paniza
*/
public class GenerarNumeroAlbaran extends ViewBaseAction {
public void execute() throws Exception {
getView().setValue("numero", new Integer(77));
}
}
Una implementación simple pero ilustrativa. Se puede usar cualquier acción definida en
controladores.xml y su funcionamiento es el normal para una acción OpenXava. En el
capítulo 7 veremos más detalles sobre los controladores.
Opcionalmente podemos hacer nuestra acción una
IPropertyAction (esto está disponible solo para acciones usadas en
@Action de propiedades, the esta forma la vista contenedora y el nombre de la propiedad son inyectados en la acción por OpenXava. La clase de la acción anterior se podría reescribir así:
package org.openxava.test.acciones;
import org.openxava.actions.*;
import org.openxava.view.*;
/**
* @author Javier Paniza
*/
public class GenerarNumeroAlbaran
extends BaseAction
implements IPropertyAction { // 1
private View view;
private String property;
public void execute() throws Exception {
view.setValue(property, new Integer(77)); // 2
}
public void setProperty(String property) { // 3
this.property = property;
}
public void setView(View view) { // 4
this.view = view;
}
}
Esta acción implementa
IPropertyAction (1), esto requiere que la clase tenga los métodos
setProperty() (3) y
setView() (4), estos valores serán inyectados en la acción antes de llamar al método
execute(), donde pueden ser usados (2). En este caso no necesitas inyectar el objeto
xava_view al definir la acción en
controladores.xml. La vista inyectada por
setView() (4) es la vista más interna que contiene la propiedad, por ejemplo, si la propiedad está dentro de un agregado es la vista de ese agregado, no la vista principal del módulo. De esta manera podemos escribir acciones más reutilizables.
Escoger un editor
Un editor visualiza la propiedad al usuario y le permite editar su valor. OpenXava usa por defecto el editor asociado al estereotipo o tipo de la propiedad, pero podemos especificar un editor concreto para visualizar una propiedad usando
@Editor.
Por ejemplo, OpenXava usa un combo para editar las propiedades de tipo
enum, pero si queremos visualizar una propiedad de este tipo en alguna vista concreta usando un radio button podemos definir esa vista de esta forma:
@Editor(forViews="TipoConRadioButton", value="ValidValuesRadioButton")
private Tipo tipo;
public enum Tipo { NORMAL, FIJO, ESPECIAL };
En este caso para visualizar/editar se usará el editor
ValidValuesRadioButton, en lugar de del editor por defecto.
ValidValueRadioButton está definido en
OpenXava/xava/default-editors.xml como sigue:
<editor name="ValidValuesRadioButton" url="radioButtonEditor.jsp"/>
Este editor está incluido con OpenXava, pero nosotros podemos crear nuestro propios editores con nuestro propios JSPs y declararlos en el archivo
xava/editores.xml de nuestro proyecto.
Esta característica es para cambiar el editor solo en una vista. Si lo que se pretende es cambiar el editor para un estereotipo, tipo o una propiedad de un modelo a nivel de aplicación entonces lo mejor es configurarlo usando el archivo
xava/editors.xml.
Personalización de referencia
Podemos refinar la forma de visualización y comportamiento de una referencia en la vista usando las siguientes anotaciones:
@ReferenceView // 1
@ReadOnly // 2
@NoFrame // 3
@NoCreate // 4
@NoModify // 5
@NoSearch // 6
@AsEmbedded // 7
@SearchAction // 8
@DescriptionsList // 9
@LabelFormat // 10
@Action // 11
@OnChange // 12
@OnChangeSearch // 13
@ManyToOne
private tipo nombreReferencia;
Todas estas anotaciones siguen las
normas para anotaciones de vista y todas ellas son opcionales. OpenXava siempre asume valores por defecto correcto si se omiten.
- @ReferenceView (OX): Si omitimos esta anotación usa la vista por defecto del objeto referenciado para visualizarlo, con este anotación podemos indicar que use otra vista.
- @ReadOnly (OX): Si usamos esta anotación esta referencia no será nunca editable por el usuario en esta vista. Una alternativa a esto es hacer la propiedad editable/no editable programáticamente usando org.openxava.view.View.
- @NoFrame (OX): El dibujador de la interfaz gráfica usa un marco para envolver todos los datos de la referencia. Con esta anotación se puede indicar que no se use ese marco.
- @NoCreate (OX): Por defecto el usuario tiene opción para crear un nuevo objeto del tipo referenciado. Con esta anotación anulamos esta posibilidad.
- @NoModify (OX): Por defecto el usuario tiene opción para modificar el objeto actualmente referenciado. Con esta anotación anulamos esta posibilidad.
- @NoSearch (OX): Por defecto el usuario tiene un vínculo para poder realizar búsquedas con una lista, filtros, etc. Con esta anotación anulamos esta posibilidad.
- @AsEmbedded (OX): Por defecto en el caso de una referencia a una clase incrustable el usuario puede crear y editar sus datos, mientras que en el caso de una referencia a una entidad el usuario escoge una entidad existente. Si ponemos @AsEmbedded entonce la interfaz de usuario para referencias a entidad se comporta como en el caso de los incrustados, permitiendo al usuario crear un nuevo objeto y editar sus datos directamente. No tiene efecto en el caso de una referencia a un objeto incrustado. ¡Ojo! Si borramos una entidad sus entidades referenciadas no se borran, incluso si estamos usando @AsEmbedded.
- @SearchAction (OX): Nos permite especificar nuestra propia acción de búsqueda cuando se pulsa al vínculo de buscar. Solo es posible una por vista.
- @DescriptionsList (OX): Permite visualizar los datos como una lista descripciones, típicamente un combo. Práctico cuando hay pocos elementos del objeto referenciado.
- @LabelFormat (OX): Formato de la etiqueta de la referencia. Solo aplica si esta referencia se ha anotado con @DescriptionsList. Funciona como en el caso de las propiedades.
- @Action (OX): Acciones (mostradas como vínculos, botones o imágenes al usuario) asociadas (visualmente) a esta referencia y que el usuario final puede ejecutar. Funciona como en el caso de las propiedades. Podemos definir varias acciones a la misma referencia en una vista.
- @OnChange (OX): Acción a realizar cuando cambia el valor de esta propiedad. Solo una acción @OnChange por vista está permitida.
- @OnChangeSearch (OX): Nos permite especificar nuestra propia acción de búsqueda cuando el usuario teclea una clave nueva. Solo es posible una por vista.
Si no usamos ninguna de estas anotaciones OpenXava dibuja la referencia usando su vista por defecto. Por ejemplo si tenemos una referencia así:
@ManyToOne
private Familia familia;
La interfaz gráfica tendrá el siguiente aspecto:
Escoger vista
La modificación más sencilla sería especificar que vista del objeto referenciado queremos usar. Esto se hace mediante
@ReferenceView:
@ManyToOne(fetch=FetchType.LAZY)
@ReferenceView("Simple")
private Factura factura;
Para esto en el componente
Factura tenemos que tener una vista llamada simple:
@Entity
@Views({
...
@View(name="Simple", members="año, numero, fecha, descuentoAño;"),
...
})
public class Factura {
Y así en lugar de usar la vista de la
Factura por defecto, que supuestamente sacará toda la información, visualizará ésta:
Personalizar el enmarcado
Si combinamos
@NoFrame con un grupo podemos agrupar visualmente una propiedad que no forma parte de la referencia, por ejemplo:
@View( members=
...
"comercial [" +
" comercial; " +
" relacionConComercial;" +
"]" +
...
)
public class Cliente {
...
@ManyToOne(fetch=FetchType.LAZY)
@NoFrame
private Comercial comercial;
...
}
Así obtendríamos:
Acción de búsqueda propia
El usuario puede buscar un nuevo valor para la referencia simplemente tecleando el código y al salir del editor recupera el valor correspondiente; por ejemplo, si el usuario teclea "1" en el campo del código de comercial, el nombre (y demás datos) del comercial "1" serán automaticamente rellenados. También podemos pulsar la linternita, en ese caso vamos a una lista en donde podemos filtrar, ordenar, etc, y marcar el objeto deseado.
Para definir nuestra propia rutina de búsqueda podemos usar
@SearchAction, como sigue:
@ManyToOne(fetch=FetchType.LAZY) @SearchAction("MiReferencia.buscar")
private Comercial comercial;
Ahora al pulsar la linternita ejecuta nuestra acción, la cual tenemos que tener definida en
controladores.xml:
<controlador nombre="MiReferencia">
<accion nombre="buscar" oculta="true"
clase="org.openxava.test.acciones.MiAccionBuscar"
imagen="images/search.gif">
<usa-objeto nombre="xava_view"/>
<usa-objeto nombre="xava_referenceSubview"/>
<usa-objeto nombre="xava_tab"/>
<usa-objeto nombre="xava_currentReferenceLabel"/>
</accion>
...
</controlador>
Lo que hagamos en
MiAccionBuscar ya es cosa nuestra. Podemos, por ejemplo, refinar la acción por defecto de busqueda para filtrar la lista usada para buscar, como sigue:
package org.openxava.test.acciones;
import org.openxava.actions.*;
/**
* @author Javier Paniza
*/
public class MiAccionBuscar extends ReferenceSearchAction {
public void execute() throws Exception {
super.execute(); // El comportamiento por defecto para buscar
getTab().setBaseCondition("${codigo} < 3"); // Añadir un filtro a la lista
}
}
Veremos más acerca de las acciones en el
capítulo 7.
Acción de creación propia
Si no hemos puesto
@NoCreate el usuario tendrá un vínculo para poder crear un nuevo objeto. Por defecto muestra la vista por defecto del componente referenciado y permite introducir valores y pulsar un botón para crearlo. Si queremos podemos definir nuestras propias acciones (entre ellas la de crear) en el formulario a donde se va para crear uno nuevo, para esto hemos de tener un controlador llamado como el componente con el sufijo Creation. Si OpenXava ve que existe un controlador así lo usa en vez del de por defecto para permitir crear un nuevo objeto desde una referencia. Por ejemplo, podemos poner en nuestro
controladores.xml:
<!--
Puesto que su nombre es AlmacenCreation (nombre modelo + Creation) es usado
por defecto para crear desde referencias, en vez de NewCreation.
La accion 'new' es ejecutada automáticamente.
-->
<controlador nombre="AlmacenCreation">
<hereda-de controlador="NewCreation"/>
<accion nombre="new" oculta="true"
clase="org.openxava.test.actions.CrearNuevoAlmacenDesdeReferencia">
<usa-objeto nombre="xava_view"/>
</accion>
</controlador>
En este caso cuando en una referencia a
Almacen pulsemos el vínculo 'crear' irá a la vista por defecto de
Almacen y mostrará las acciones de
AlmacenCreation.
Sí tenemos una acción
new, ésta se ejecuta automáticamente antes de nada, la podemos usar para iniciar la vista si lo necesitamos.
Acción de modificación propia
Si no hemos puesto
@NoModify el usuario tendrá un vínculo para poder actualizar el objeto actualmente referenciado. Por defecto muestra la vista por defecto del componente referenciado y permite modificar valores y pulsar un botón para actualizarlo. Si queremos podemos definir nuestras propias acciones (entre ellas la de actualizar) en el formulario a donde se va para modificar, para esto hemos de tener un controlador llamado como el componente con el sufijo
Modification. Si OpenXava ve que existe un controlador así lo usa en vez del de por defecto para permitir modificar el objeto referenciado desde una referencia. Por ejemplo, podemos poner en nuestro
controladores.xml:
<!--
Dado que su nombre es AlmacenModification (nombre modelo + Modification) es usado
por defecto para modificar desde referencias, en lugar de Modification.
La acción 'search' se ejecuta automáticamente.
-->
<controlador nombre="AlmacenModification">
<hereda-de controlador="Modification"/>
<accion nombre="search" oculta="true"
clase="org.openxava.test.actions.ModificarAlmacenDesdeReferencia">
<usa-objeto nombre="xava_view"/>
</accion>
</controlador>
En este caso cuando en una referencia a
Almacen pulsemos el vínculo 'modificar' irá a la vista por defecto de
Almacen y mostrará las acciones de
AlmacenModification.
Sí tenemos una acción
search, ésta se ejecuta automáticamente antes de nada, la podemos usar para iniciar la vista con los datos del objeto actualmente referenciado.
Lista descripciones (combos)
Con
@DescriptionsList podemos instruir a OpenXava para que visualice la referencia como una lista de descripciones (actualmente como un combo). Esto puede ser práctico cuando hay pocos valores y haya un nombre o descripción significativo. La sintaxis es:
@DescriptionsList(
descriptionProperties="propiedades", // 1
depends="depende de", // 2
condition="condición", // 3
orderByKey="true|false", // 4
order="orden" // 5
)
- descriptionProperties (opcional): Indica que propiedad o propiedades tienen que aparecer en la lista, si no se especifica asume la propiedad description, descripcion, name o nombre. Si el objeto referencia no tiene ninguna propiedad llamada así entonces es obligado especificar aquí un nombre de propiedad. Permite poner una lista de propiedades separadas por comas. Al usuario le aparecen concatenadas.
- depends (opcional): Se usa junto con condition para hacer que el contenido de la lista dependa del valor de otro miembro visualizado en la vista principal (si simplemente ponemos el nombre del miembro) o en la misma vista (si ponemos this. delante del nombre de miembro).
- condition (opcional): Permite poner una condición (al estilo SQL) para filtrar los valores que aparecen en la lista de descripciones.
- orderByKey (opcional): Por defecto los datos salen ordenados por descripción, pero si ponemos está propiedad a true saldrán ordenados por clave.
- order (opcional): Permite poner un orden (al estilo SQL) para los valores que aparecen en la lista de descripciones.
El uso más simple es:
@ManyToOne(fetch=FetchType.LAZY)
@DescriptionsList
private Almacen almacen;
Que haría que una referencia a
Almacen se representara así:
En un principio saca todos los almacenes, aunque en realidad usa la
baseCondition y
filter especificados en el
@Tab por defecto de
Almacen. Veremos como funcionan los tabs en el
capítulo 5.
Si queremos, por ejemplo, que se visualice un combo con las familias de productos y según la familia que se escoja se rellene el combo de las subfamilias, podemos hacer algo así:
@ManyToOne(fetch=FetchType.LAZY)
@DescriptionsList(orderByKey=true) // 1
private Familia familia;
@ManyToOne(fetch=FetchType.LAZY) @NoCreate // 2
@DescriptionsList(
descriptionProperties="descripcion", // 3
depends="familia", // 4
condition="${familia.codigo} = ?" // 5
order="${descripcion} desc" // 6
)
private Subfamilia subfamilia;
Se visualizarán 2 combos uno con todas las familias y otro vacío, y al seleccionar una familia el otro combo se rellenará con todas las subfamilias de esa familia.
En el caso de
Familia (1) se visualiza la propiedad
descripcion de
Familia, ya que si no lo indicamos por defecto visualiza una propiedad llamada
'descripcion' o
'nombre'. En este caso los datos aparecen ordenados por clave y no por descripción. En el caso de
Subfamilia indicamos que no muestre el vínculo para crear una nueva subfamilia (2) y que la propiedad a visualizar es
descripcion (aunque esto lo podríamos haber omitido). Con
depends (4) hacemos que este combo dependa de la referencia
familia, cuando cambia
familia en la interfaz gráfica, rellenará esta lista de descripciones aplicando la condición de
condition (5) y enviando como argumento (para rellenar el interrogante) el nuevo valor de familia. Y las entradas están ordenadas descendentemente por
descripcion (6).
En
condition y
order ponemos los nombres de las propiedades entre
${} y los argumentos como
?, los operadores de comparación son los de SQL.
Podemos especificar una lista de propiedades para que aparezca como descripción:
@ManyToOne(fetch=FetchType.LAZY)
@ReadOnly
@DescriptionsList(descriptionProperties="nivel.descripcion, nombre")
private Comercial comercialAlternativo;
En este caso en el combo se visualizará una concatenación de la descripción del nivel y el nombre. Además vemos como podemos usar propiedades calificadas (
nivel.descripcion) también.
En el caso de poner una referencia lista descripciones (
@DescriptionsList) como solo lectura (
@ReadOnly) se visualizará la descripción (en este caso
nivel.descripcion + nombre) como si fuera una propiedad simple de texto y no como un combo.
Evento de cambio de valor de referencia
Si queremos reaccionar al evento de cambio de valor de una propiedad podemos poner:
@ManyToOne(fetch=FetchType.LAZY)
@OnChange(AlCambiarTransportistaEnAlbaran.class)
private Transportista transportista;
En este caso nuestra acción escucha al cambio del código de transportista.
El código a ejecutar es:
package org.openxava.test.acciones;
import org.openxava.actions.*;
/**
* @author Javier Paniza
*/
public class AlCambiarTransportistaEnAlbaran
extends OnChangePropertyBaseAction { // 1
public void execute() throws Exception {
if (getNewValue() == null) return;
getView().setValue("observaciones",
"El transportista es " + getNewValue());
addMessage("transportista_cambiado");
}
}
La acción implementa
IOnChangePropertyAction, mediante
OnChangePropertyBaseAction (1), aunque es una referencia. Recibimos el cambio de la propiedad clave de la referencia; en este caso
transportista.codigo. El resto es como en
el caso de una propiedad.
Búsqueda de referencia al cambiar
El usuario puede buscar el valor de una referencia simplemente tecleando su clave. Por ejemplo, si hay una referencia a
Subfamilia, el usuario puede teclear el código de subfamilia y automáticamente se cargará la información de la subfamilia en la vista. Esto se hace usando una acción "al cambiar" que hace la búsqueda. Podemos especificar nuestra propia acción para buscar cuando la clave cambia usando la anotación
@OnChangeSearch, justo así:
@ManyToOne(fetch=FetchType.LAZY)
@OnChangeSearch(BuscarAlCambiarSubfamilia.class)
private Subfamilia subfamilia;
Esta acción se ejecuta para realizar la búsqueda, en vez de la acción por defecto, cuando el usuario cambia el código de subfamilia.
El código a ejecutar es:
package org.openxava.test.acciones;
import org.openxava.actions.*;
/**
*
* @author Javier Paniza
*/
public class BuscarAlCambiarSubfamilia
extends OnChangeSearchAction { // 1
public void execute() throws Exception {
if (getView().getValueInt("codigo") == 0) {
getView().setValue("codigo", new Integer("1"));
}
super.execute();
}
}
La acción implementa
IOnChangePropertyAction, mediante
OnChangeSearchAction (1), aunque es una referencia. Recibe el cambio de la propiedad clave de la referencia; en este caso
subfamilia.codigo.
Este caso es un ejemplo de refinamiento del comportamiento de la búsqueda al cambiar, porque extiende de
OnChangeSearchAction, que es la acción por defecto para buscar, y llama a
super.execute(). También es posible hacer una acción al cambiar convencional (extendiendo de
OnChangePropertyBaseAction por ejemplo) anulando completamente la lógica de búsqueda.
Personalización de colección
Podemos refinar la forma de visualización y comportamiento de una colección en la vista usando las siguientes anotaciones:
@CollectionView // 1
@ReadOnly // 2
@EditOnly // 3
@NoCreate // 4
@NoModify // 5
@AsEmbedded // 6
@ListProperties // 7
@RowStyle // 8
@EditAction // 9
@ViewAction // 10
@NewAction // 11
@SaveAction // 12
@HideDetailAction // 13
@RemoveAction // 14
@RemoveSelectedAction // 15
@ListAction // 16
@DetailAction // 17
@OneToMany/@ManyToMany
private Collection nombreColeccion;
Todas estas anotaciones siguen las
normas para anotaciones de vista y todas ellas son opcionales. OpenXava siempre asume valores por defecto correcto si se omiten.
- @CollectionView (OX): La vista del objeto referenciado que se ha de usar para representar el detalle. Por defecto usa la vista por defecto.
- @ReadOnly (OX): Si la ponemos solo podremos visualizar los elementos de la colección, no podremos ni añadir, ni borrar, ni modificar los elementos.
- @EditOnly (OX): Si la ponemos podemos modificar los elementos existentes, pero no podemos añadir nuevos ni eliminar.
- @NoCreate (OX): Si la ponemos el usuario final no tendrá el vínculo que le permite crear objetos del tipo del objeto referenciado. No aplica a colecciones incrustadas.
- @NoModify (OX): Si la ponemos el usuario final no tendrá el vínculo que le permite modificar objetos del tipo del objeto referenciado. No aplica a colecciones incrustadas.
- @AsEmbedded (OX): Por defecto las colecciones incrustadas permiten al usuario crear y añadir elementos, mientras que las colecciones convencionales permiten solo escoger entidades existentes para añadir (o quitar) de la colección. Si ponemos @AsEmbedded entonces la colección de entidades se comportan como una colección de agregados, permitiendo al usuario añadir objetos y editarlos directamente. No tiene efecto en el caso de una colección incrustada.
- @ListProperties (OX): Indica las propiedades que han de salir en la lista al visualizar la colección. Podemos calificar las propiedades. Por defecto saca todas las propiedades persistentes del objeto referenciado (sin incluir referencias ni calculadas). Solo una @ListProperties por vista está permitida.
- @RowStyle (OX): Para dar un estilo especial a algunas filas. Se comporta igual que en el caso del Tab. No funciona para colecciones calculadas. Es posible definir varias @RowStyle por cada vista.
- @EditAction (OX): Permite sobreescribir la acción que inicia la edición de un elemento de la colección. Esta es la acción mostrada en cada fila cuando la colección es editable. Solo una @EditAction por vista está permitida.
- @ViewAction (OX): Permite sobreescribir la acción para visualizar un elemento de la colección. Esta es la acción mostrada en cada fila cuando la colección es de solo lectura. Solo una @ViewAction por vista está permitida.
- @NewAction (OX): Permite definir nuestra propia acción para empezar a añadir un nuevo elemento en la colección. Ésta es la acción que se ejecuta al pulsar en el vínculo 'Añadir'. Solo una @NewAction por vista está permitida.
- @SaveAction (OX): Permite definir nuestra propia acción para grabar el elemento de la colección. Ésta es la acción que se ejecuta al pulsar el vínculo 'Grabar detalle'. Solo una @SaveAction por vista está permitida.
- @HideDetailAction (OX): Permite definir nuestra propia acción para ocultar la vista de detalle. Ésta es la acción que se ejecuta al pulsar el vínculo 'Cerrar'. Solo una @HideDetailAction por vista está permitida.
- @RemoveAction (OX): Permite definir nuestra propia acción para borrar un elemento de la colección. Ésta es la acción que se ejecuta al pulsar en el vínculo 'Quitar detalle'. Solo una @RemoveAction por vista está permitida.
- @RemoveSelectedAction (OX): Permite definir nuestra propia acción para quitar los elementos seleccionados de la colección. Ésta es la acción que se ejecuta al seleccionar algunas filas y pulsar en el vínculo 'Quitar seleccionados'. Solo una @RemoveSelectedAction por vista está permitida.
- @ListAction (OX): Para poder añadir acciones en el modo lista; normalmente acciones cuyo alcance es la colección entera. Es posible definir varias @ListAction por cada vista.
- @DetailAction (OX): Para poder añadir acciones en detalle, normalmente acciones cuyo alcance es el detalle que se está editando. Es posible definir varias @DetailAction por cada vista.
Si no usamos ninguna de estas anotaciones una colección se visualiza usando las propiedades persistentes en el modo lista y la vista por defecto para representar el detalle; aunque lo más normal es indicar como mínimo que propiedades salen en la lista y que vista se ha de usar para representar el detalle:
@CollectionView("Simple"),
@ListProperties("codigo, nombre, observaciones, relacionConComercial, comercial.nivel.descripcion, tipo")
@OneToMany(mappedBy="comercial")
private Collection<Cliente> clientes;
De esta forma la colección se visualiza así:
Podemos ver como en la lista de propiedades podemos poner propiedades calificadas (como
comercial.nivel.descripcion).
Al pulsar
('Editar') se visualizará el detalle usando la vista
Simple de
Cliente; para eso
hemos de tener una vista llamada
Simple en la entidad
Cliente (el modelo de los elementos de la colección).
Este vista se usa también cuando el usuario pulsa en
'Añadir' en una
colección incrustada, en caso contrario OpenXava no muestra esta vista, en su lugar muestra una lista de entidades a añadir.
Si la vista
Simple de
Cliente es así:
@View(name="Simple", members="codigo; tipo; nombre; direccion")
Al pulsar detalle aparecerá:
Acción de editar/ver detalle propia
Podemos refinar fácilmente el comportamiento cuando se pulse el vínculo
('Editar') usando
@EditAction:
@EditAction("Factura.editarLinea")
@OneToMany (mappedBy="factura", cascade=CascadeType.REMOVE)
private Collection<LineaFactura> lineas;
Hemos de definir
Factura.editarLinea en
controladores.xml:
<controlador nombre="Factura">
...
<accion nombre="editarLinea" oculta="true"
imagen="images/edit.gif"
clase="org.openxava.test.acciones.EditarLineaFactura">
<usa-objeto nombre="xava_view"/>
</accion>
...
</controlador>
Y nuestra acción puede ser así:
package org.openxava.test.acciones;
import java.text.*;
import org.openxava.actions.*;
/**
* @author Javier Paniza
*/
public class EditarLineaFactura extends EditElementInCollectionAction { // 1
public void execute() throws Exception {
super.execute();
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
getCollectionElementView().setValue( // 2
"observaciones", "Editado el " + df.format(new java.util.Date()));
}
}
En este caso queremos solamente refinar y por eso nuestra acción desciende de (1)
EditElementInCollectionAction. Nos limitamos a poner un valor por defecto en la propiedad
remarks. Es de notar que para acceder a la vista que visualiza el detalle podemos usar el método
getCollectionElementView() (2).
También es posible eliminar la acción para editar de la interfaz de usuario, de esta manera:
@EditAction("")
@OneToMany (mappedBy="factura", cascade=CascadeType.REMOVE)
private Collection<LineaFactura> lineas;
Sólo necesitamos poner una cadena vacía como valor para la acción. Aunque en la mayoría de los casos es suficiente declarar la colección como de solo lectura (
@ReadOnly).
La técnica para refinar una acción 'ver' (la acción para cada fila cuando la colección es de solo lectura) es la misma pero usando
@ViewAction en vez de
@EditAction.
Acciones de lista propias
Añadir nuestras propias acciones de lista (acciones que aplican a la colección entera) es fácil con
@ListAction:
@ListAction("Transportista.traducirNombre"),
private Collection<Transportista> compañeros;
Ahora aparecen un nuevo vínculo al usuario:
Falta definir la acción en
controladores.xml:
<controlador nombre="Transportista">
...
<accion nombre="traducirNombre" oculta="true"
clase="org.openxava.test.actiones.TraducirNombreTransportista">
</accion>
...
</controlador>
Y el código de nuestra acción:
package org.openxava.test.acciones;
import java.util.*;
import org.openxava.actions.*;
import org.openxava.test.modelo.*;
/**
* @author Javier Paniza
*/
public class TraducirNombreTransportista extends CollectionBaseAction { // 1
public void execute() throws Exception {
Iterator it = getSelectedObjects().iterator(); // 2
while (it.hasNext()) {
Transportista transportista = (Transportista) it.next();
transportista.traducir();
}
}
}
La acción desciende de
CollectionBaseAction (1), de esta forma tenemos a nuestra disposición métodos como
getSelectedObjects() (2) que ofrece una colección de los objetos seleccionados por el usuario. Hay disponible otros métodos como
getObjects() (todos los objetos de la colección),
getMapValues() (los valores de la colección en formato de mapa) y
getMapsSelectedValues() (los valores seleccionados de la colección en formato de mapa).
Como en el caso de la acciones de detalle (ver la siguiente sección) puedes usar
getCollectionElementView().
También es posible usar
acciones para el modo lista como acciones de lista para una colección.
Acciones de lista por defecto
Si queremos añadir alguna acciones de lista a todas las colecciones de nuestra aplicación hemos de crear un controlador llamado
DefaultListActionsForCollections en nuestro propio
xava/controladores.xml como sigue:
<controlador nombre="DefaultListActionsForCollections">
<hereda-de controlador="Print"/>
<accion nombre="exportarComoXML"
clase="org.openxava.test.acciones.ExportarComoXML">
</accion>
</controlador>
De esta forma todas las colecciones tendrán las acciones del controlador
Print (para exportar a Excel y generar informes PDF) y nuestra propia acción
ExportarComoXML. Esto tiene el mismo efecto que el elemento
@ListAction (ver la sección
acciones de lista propias) pero aplica a todas las colecciones a la vez.
Esta característica no aplica a las colecciones calculadas.
Acciones de detalle propias
También podemos añadir nuestras propias acciones a la vista de detalle usada para editar cada elemento. Esto se consigue mediante la anotación
@DetailAction. Estas sería acciones que aplican a un solo elemento de la colección. Por ejemplo:
@DetailAction("Factura.verProducto")
@OneToMany (mappedBy="factura", cascade=CascadeType.REMOVE)
private Collection<InvoiceDetail> lineas;
Esto haría que el usuario tuviese a su disposición otro vínculo al editar el detalle:
Debemos definir la acción en
controladores.xml:
<controlador nombre="Facturas">
...
<accion nombre="verProducto" oculta="true"
clase="org.openxava.test.acciones.VerProductoDesdeLineaFactura">
<usa-objeto nombre="xava_view"/>
<use-objeto nombre="xavatest_valoresFactura"/>
</accion>
...
</controlador>
Y el código de nuestra acción:
package org.openxava.test.acciones;
import java.util.*;
import javax.ejb.*;
import org.openxava.actions.*;
/**
* @author Javier Paniza
*/
public class VerProductoDesdeLineaFactura
extends CollectionElementViewBaseAction // 1
implements INavigationAction {
private Map valoresFactura;
public void execute() throws Exception {
try {
setValoresFactura(getView().getValues());
Object codigo =
getCollectionElementView().getValue("producto.codigo"); // 2
Map clave = new HashMap();
clave.put("codigo", codigo);
getView().setModelName("Producto"); // 3
getView().setValues(clave);
getView().findObject();
getView().setKeyEditable(false);
getView().setEditable(false);
}
catch (ObjectNotFoundException ex) {
getView().clear();
addError("object_not_found");
}
catch (Exception ex) {
ex.printStackTrace();
addError("system_error");
}
}
public String[] getNextControllers() {
return new String [] { "ProductoDesdeFactura" };
}
public String getCustomView() {
return SAME_VIEW;
}
public Map getValoresFactura() {
return valoresFactura;
}
public void setValoresFactura(Map map) {
valoresFactura = map;
}
}
Vemos como desciende de
CollectionElementViewBaseAction (1) y así tiene disponible la vista que visualiza el elemento de la colección mediante
getCollectionElementView() (2). También podemos acceder a la vista principal mediante
getView() (3). En el
capítulo 7 se ven más detalles acerca de como escribir acciones.
Además, usando la vista devuelta por
getCollectionElementView() podemos añadir y borrar programaticamente acciones de detalle y de lista con
addDetailAction(),
removeDetailAction(),
addListAction() y
removeListAction(), ver API doc para
org.openxava.view.View.
Refinar comportamiento por defecto para la vista de colección
Usando
@NewAction,
@SaveAction,
@HideDetailAction,
@RemoveAction y
@RemoveSelectedAction podemos refinar el comportamiento por defecto para una vista de colección. Por ejemplo, si queremos refinar el comportamiento de la acción de grabar un detalle podemos definir nuestra vista de esta forma:
@SaveAction("LineaAlbaran.grabar")
@OneToMany (mappedBy="albaran", cascade=CascadeType.REMOVE)
private Collection<LineaAlbaran> lineas;
Debemos tener la acción
LineaAlbaran.grabar en
controladores.xml:
<controlador nombre="LineaAlbaran">
...
<accion nombre="grabar"
clase="org.openxava.test.acciones.GrabarLineaAlbaran">
<usa-objeto nombre="xava_view"/>
</accion>
...
</controlador>
Y definir la clase acción para grabar:
package org.openxava.test.acciones;
import org.openxava.actions.*;
/**
* @author Javier Paniza
*/
public class GrabarDetalleAlbaran extends SaveElementInCollectionAction { // 1
public void execute() throws Exception {
super.execute();
// Aquí nuestro código // 2
}
}
El caso más común es extender el comportamiento por defecto, para eso hemos de extender la clase original para grabar un detalle de una colección (1), esto es la acción
SaveElementInCollection, entonces llamamos a
super desde el método
execute() (2), y después escribimos nuestro propio propio código.
También es posible eliminar cualquiera de estas acciones de la interfaz gráfica, por ejemplo, podemos defina una colección de esta manera:
@RemoveSelectedAction("")
@OneToMany (mappedBy="albaran", cascade=CascadeType.REMOVE)
private Collection<LineaAlbaran> lineas;
En este caso la acción para quitar los elementos seleccionadas no aparecerá en la interfaz de usuario. Como se ve, sólo es necesario declarar una cadena vacía como nombre de la acción.
Propiedades transitorias para controles gráficos
Con
@Transient (JPA) podemos usar una propiedad que no se guarde en la base de datos, pero que sí nos interesa que se visualice al usuario. Podemos usarlas para proporcionar controles al usuario para manejar la interfaz gráfica.
Un ejemplo:
@Transient
@DefaultValueCalculator(value=EnumCalculator.class,
properties={
@PropertyValue(name="enumType", value="org.openxava.test.modelo.Albaran$EntregadoPor")
@PropertyValue(name="value", value="TRANSPORTISTA")
}
)
@OnChange(AlCambiarEntradoPor.class)
private EntragadoPor entregadoPor;
public enum EntregadoPor { TRABAJADOR, TRANSPORTISTA }
Podemos observar como la sintaxis es exactamente igual que en el caso de definir una propiedad en la parte del modelo, podemos incluso hacer que sea un
enum y que tenga un
@DefaultValueCalculator. Después de haber definido la propiedad podemos usarla en la vista como una propiedad más, asignandole una acción
@OnChange por ejemplo y por supuesto poniendola como miembro de una vista.
Acciones de la vista
Además de poder asociar acciones a una propiedad, referencia o colección, podemos tambien definir acciones arbitrarias en cualquier parte de nuestra vista. Para poder hacer esto se ponemos el nombre calificado de la acción seguido de paréntesis (), de esta manera:
@View( members=
"codigo;" +
"tipo;" +
"nombre, Cliente.cambiarEtiquetaDeNombre();" +
...
El efecto visual sería:
Podemos ver el vínculo 'Cambiar nombre de etiqueta' que ejecutará la acción
Clientes.cambiarEtiquetaDeNombre al pulsarlo.
Si la vista contenedora de la acción no es editable, la acción no estará presente. Si queremos que la acción esté siempre activa, incluso si la vista no está editable, hemos de usar poner la palabra ALWAYS entre los paréntesis, como sigue:
@View( members=
"codigo;" +
"tipo;" +
"nombre, Cliente.cambiarEtiquetaDeNombre(ALWAYS);" +
...
La forma normal de exponer las acciones al usuario es mediante los controladores (acciones en la barra), lo controladores son reutilizables entre vistas, pero puede que a veces necesitemos una acción específica a una vista, y queramos visualizarla dentro de la misma (no en la barra de botones), para estos casos el elemento accion puede ser útil.
Podemos ver más acerca de las acciones en el
capítulo 7.
Clase transitoria: Solo para crear vistas
En OpenXava no se puede tener vistas que no estén asociadas a un modelo. Así que si queremos dibujar una interfaz gráfica arbitraria, lo que hemos de hacer es crear una clase, no marcarla como entidad y a partir de ésta definir una vista.
Una clase transitoria no está asociada a ninguna tabla de la base de datos, normalmente se usa solo para visualizar interfaces de usuario no relacionadas con ninguna tabla de la base de datos.
Un ejemplo puede ser:
package org.openxava.test.model;
import javax.persistence.*;
import org.openxava.annotations.*;
/**
* Ejemplo de una clase OpenXava transitoria (no persistente) del modelo. <p>
*
* Esto se puede usar, por ejemplo, para visualizar un diálogo,
* o cualquier otro interfaz gráfica.<p>
*
* Notemos como no está marcada con @Entity <br>
*
* @author Javier Paniza
*/
@Views({
@View(name="Familia1", members="subfamilia"),
@View(name="Familia2", members="subfamilia"),
@View(name="ConFormularioSubfamilia", members="subfamilia"),
@View(name="Rango", members="subfamilia; subfamiliaHasta")
})
public class FiltroPorSubfamilia {
@ManyToOne(fetch=FetchType.LAZY) @Required
@NoCreate(forViews="Familia1, Familia2")
@NoModify(forViews="Familia2, ConFormularioSubfamilia")
@NoSearch(forViews="ConFormularioSubfamilia")
@DescriptionsLists({
@DescriptionsList(forViews="Familia1",
condition="${familia.codigo} = 1", order="${codigo} desc"
),
@DescriptionsList(forViews="Familia2",
condition="${familia.codigo} = 2"
)
})
private Subfamilia subfamilia;
@ManyToOne(fetch=FetchType.LAZY)
private Subfamilia subfamiliaHasta;
public Subfamilia getSubfamilia() {
return subfamilia;
}
public void setSubfamilia(Subfamilia subfamilia) {
this.subfamilia = subfamilia;
}
public Subfamilia getSubfamiliaHasta() {
return subfamiliaHasta;
}
public void setSubfamiliaHasta(Subfamilia subfamiliaHasta) {
this.subfamiliaHasta = subfamiliaHasta;
}
}
Para definir una clase del modelo como transitorio solo necesitamos definir una clase convencional sin
@Entity. No hemos de poner el mapeo ni declarar propiedades como clave.
De esta forma podemos hacer un diálogo que puede servir, por ejemplo, para lanzar un listado de familias o productos filtrado por subfamilias.
Podemos así tener un generador de cualquier tipo de interfaz gráficas sencillo y bastante flexible, aunque no queramos que la información visualizada sea persistente.