1.Visión general | 2.Mi primer proyecto | 3.Modelo | 4.Vista | 5.Datos tabulares | 6.Mapeo objeto/relacional | 7.Controladores | 8.Aplicación | 9.Personalización

Capítulo 9: Personalización

La interfaz de usuario generada por OpenXava es buena para la mayoría de los casos, pero a veces puede que necesitemos personalizar alguna parte de la interfaz de usuario (creando nuestros propios editores) o crear nuestra interfaz de usuario íntegramente a mano (usando vistas personalizadas con JSP).
Por otra parte, si simplemente quieres definir la apariencia visual de tu aplicación, mira la guía para definir un Estilo visual personalizado.

Editores

Configuración de editores

Vemos como el nivel de abstracción usado para definir las vista es alto, nosotros especificamos las propiedades que aparecen y como se distribuyen, pero no cómo se visualizan. Para visualizar las propiedades OpenXava utiliza editores.
Un editor indica como visualizar una propiedad. Consiste en una definición XML junto con un fragmento de código JSP.
Para refinar el comportamiento de los editores de OpenXava o añadir los nuestros podemos crear en el directorio xava de nuestro proyecto un archivo llamado editores.xml. Este archivo es como sigue:
<?xml version = "1.0" encoding = "ISO-8859-1"?>
 
<!DOCTYPE editores SYSTEM "dtds/editores.dtd">
 
<editores>
 <editor .../> ...
</editores>
Simplemente contiene la definición de un conjunto de editores, y un editor se define así:
<editor
 nombre="nombre"                         <!--  1 Nuevo en v2.1.3 -->
 url="url"                               <!--  2 -->
 formatear="true|false"                  <!--  3 -->
 depende-de-estereotipos="estereotipos"  <!--  4 -->
 depende-de-propiedades="propiedades"    <!--  5 -->
 enmarcable="true|false"                 <!--  6 -->
 recargar-siempre="true|false"           <!--  7 Nuevo en v3.1.2 -->
 compuesto="true|false"                  <!--  8 Nuevo en v3.1.3 -->
>
 <propiedad ... /> ...                   <!--  9 -->
 <formateador ... />                     <!-- 10 -->
 <formateador-lista ... />               <!-- 11 Nuevo en v3.1.4 -->
 <para-estereotipo ... /> ...            <!-- 12 -->
 <para-tipo ... /> ...                   <!-- 13 -->
 <para-propiedad-modelo ... /> ...       <!-- 14 -->
 <para-referencia ... /> ...             <!-- 15 Nuevo en v3.1.3 -->
 <para-coleccion ... /> ...              <!-- 16 Nuevo en v3.1.3 -->
 <para-valores-posibles />               <!-- 17 Nuevo en v2.1.2 -->
 <para-referencias />                    <!-- 18 Nuevo en v3.1.3 -->
 <para-colecciones />                    <!-- 19 Nuevo en v3.1.3 -->
 <para-tabs />                           <!-- 20 Nuevo en v4.6 -->
</editor>
  1. nombre (opcional): (Nuevo en v2.1.3) Nombre para referenciar a este editor desde otros sitios, por ejemplo desde @Editor en una entidad JPA o desde <vista-referencia ... editor=/> desde un componente XML.
  2. url (obligado): URL de la página JSP que implementa el editor. Empieza desde xava/editors (dentro de la carpeta web de nuestro proyecto).
  3. formatear (opcional): Si es true es OpenXava el que tiene la responsabilidad de formatear los datos desde HTML hasta Java y viceversa, si vale false tiene que hacerlo el propio editor (generalmente recogiendo información del request y asignandolo a org.openxava.view.View y viceversa). Por defecto vale true.
  4. depende-de-estereotipos (opcional): Lista de estereotipos separados por comas de los cuales depende este editor. Si en la misma vista hay algún editor para estos estereotipos éstos lanzarán un evento de cambio si cambian.
  5. depende-de-propiedades (opcional): Lista de propiedades separadas por comas de los cuales depende este editor. Si en la misma vista se está visualizando alguna de estas propiedades éstas lanzarán un evento de cambio si cambian.
  6. enmarcable (opcional): Si vale true enmarca visualmente el editor. Por defecto vale false. Es útil para cuando hacemos editores grandes (de más de una línea) que pueden quedar más bonitos de esta manera.
  7. recargar-siempre (opcional): (Nuevo en v3.1.2) Si es true, este editor se recarga siempre (cada vez que el usuario ejecuta una acción o hace cualquier otra petición a la aplicación). Cuando vale false el editor solo se recarga cuando el dato que está representado ha sido cambiado. Por defecto vale false.
  8. compuesto (opcional): (Nuevo en v3.1.3) Una editor compuesto está formado por otros editores; recibe un objeto View que reepresenta una subvista. Por defecto vale false.
  9. propiedad (varias, opcional): Permite enviar valores al editor, de esta forma podemos configurar un editor y poder usarlo en diferente situaciones.
  10. formateador (uno, opcional): Clase java para definir la conversión de Java a HTML y de HTML a Java.
  11. formateador-lista (uno, opcional): (Nuevo en v3.1.4) Clase java para definir la conversión de Java a HTML en modo lista.
  12. para-estereotipo (varias, opcional): Asocia este editor a un estereotipo. La preferencia es: un editor a una propiedad de un modelo, después por estereotipo y como último por tipo.
  13. para-tipo (varias, opcional): Asocia este editor a un tipo. La preferencia es: un editor a una propiedad de un modelo, después por estereotipo y como último por tipo.
  14. para-propiedad-modelo (varias, opcional): Asocia este editor a una propiedad concreta de un modelo. La preferencia es: un editor a una propiedad de un modelo, después por estereotipo y como último por tipo.
  15. para-referencia (varios, opcional): (Nuevo en v3.1.3) Este editor se usará para las referencias al modelo especificado.
  16. para-coleccion (varios, opcional): (Nuevo en v3.1.3) Este editor se usará para las colecciones de objetos del modelo especificado.
  17. para-valores-posibles (uno, opcional): (Nuevo en v2.1.2) Este será el editor por defecto para enum y <valores-posibles/>.
  18. para-referencias (uno, opcional): (Nuevo en v3.1.3) Este será el editor por defecto para las referencias.
  19. para-colecciones (uno, opcional): (Nuevo en v3.1.3) Este será el editor por defecto para las colecciones.
  20. para-tabs (uno, opcional): (Nuevo en v4.6) Este será el editor por defecto para las colecciones.
Podemos ver un ejemplo de definición de editor, este ejemplo es uno de los editores que vienen incluidos con OpenXava, pero es un buen ejemplo para aprender como hacer nuestros propios editores:
<editor url="textEditor.jsp">
 <for-type type="java.lang.String"/>
 <for-type type="java.math.BigDecimal"/>
 <for-type type="int"/>
 <for-type type="java.lang.Integer"/>
 <for-type type="long"/>
 <for-type type="java.lang.Long"/>
</editor>
Aquí asignamos a un grupo de tipos básicos el editor textEditor.jsp (podemos encontrarlo en la carpeta web/xava/editors de nuestro proyecto). El código JSP de este editor es:
<%@ page import="org.openxava.model.meta.MetaProperty" %>
 
<%
String propertyKey = request.getParameter("propertyKey"); // 1
MetaProperty p = (MetaProperty) request.getAttribute(propertyKey); // 2
String fvalue = (String) request.getAttribute(propertyKey + ".fvalue"); // 3
String align = p.isNumber()?"right":"left"; // 4
boolean editable="true".equals(request.getParameter("editable")); // 5
String disabled=editable?"":"disabled"; // 5
String script = request.getParameter("script"); // 6
boolean label = org.openxava.util.XavaPreferences.getInstance().isReadOnlyAsLabel();
if (editable || !label) { // 5
%>
<input id="<%=propertyKey%>" name="<%=propertyKey%>" class=editor <!-- 1 -->
 type="text"
 tabindex="1" <!-- 7 -->
 title="<%=p.getDescription(request)%>"
 align='<%=align%>' <!-- 4 -->
 maxlength="<%=p.getSize()%>"
 size="<%=p.getSize()%>"
 value="<%=fvalue%>" <!-- 3 -->
 <%=disabled%> <!-- 5 -->
 <%=script%> <!-- 6 -->
 />
<%
} else {
%>
<%=fvalue%>&nbsp;
<%
}
%>
<% if (!editable) { %>
 <input type="hidden" name="<%=propertyKey%>" value="<%=fvalue%>">
<% } %>
Un editor JSP recibe un conjunto de parámetros y tiene accesos a atributos que le permiten configurarse adecuadamente para encajar bien en una vista OpenXava. En primer lugar vemos como cogemos propertyKey (1) que después usaremos como id HTML. A partir de ese id podemos acceder a la MetaProperty (2) (que contiene toda la meta información de la propiedad a editar). El atributo fvalue(3) contiene el valor ya formateado y listo para visualizar. Averiguamos también la alineación (4) y si es o no editable (5). También recibimos el trozo de script de javascript (6) que hemos de poner en el editor.
Hemos de especificar tabindex="1" (7) para que el editor aparezca en el orden correcto de tabulación (nuevo en v4.5.1).
Aunque crear un editor directamente con JSP es sencillo no es una tarea muy habitual, es más habitual configurar JSPs ya existentes. Por ejemplo si en nuestro xava/editores.xml ponemos:
<editor url="textEditor.jsp">
 <formatedor clase="org.openxava.formatters.UpperCaseFormatter"/>
 <para-tipo tipo="java.lang.String"/>
</editor>
Estaremos sobreescribiendo el comportamiento de OpenXava para las propiedades de tipo String, ahora todas las cadenas se visualizaran y aceptaran en mayúsculas. Podemos ver el código del formateador:
package org.openxava.formatters;
 
import javax.servlet.http.*;
 
/**
 * @author Javier Paniza
 */
 
public class UpperCaseFormatter implements IFormatter { // 1
 
 public String format(HttpServletRequest request, Object string) { // 2
 return string==null?"":string.toString().toUpperCase();
 }
 
 public Object parse(HttpServletRequest request, String string) { // 3
 return string==null?"":string.toString().toUpperCase();
 }
 
}
Un formateador ha de implementar IFormatter (1) lo que lo obliga a tener un método format() (2) que convierte el valor de la propiedad que puede ser un objeto Java cualquiera en una cadena para ser visualizada en un documento HTML; y un método parse() (3) que convierte la cadena recibida de un submit del formulario HTML en un objeto Java listo para asignar a la propiedad.
También podemos establecer un formateador especifico para modo lista (Nuevo en v3.1.4), para esto disponemos de formateador-lista. El formateador asignado a este atributo nos indicará la forma en la cual se visualizará la información en el modo lista sin afectar al modo detalle. Este formateador implementará IFormatter pero a diferencia del anterior solo será necesario implementar format(). Si no se especifica formateador-lista los datos de la lista son formateados con formateador.

Editores para valores múltiples

Definir un editor para editar valores múltiples es parecido a hacerlo para valores simples. Veamos.
Por ejemplo, si queremos definir un estereotipo REGIONES que permita al usuario seleccionar más de una región para una propiedad. Ese estereotipo se puede usar de esta manera:
@Stereotype("REGIONES")
private String [] regiones;
Entonces podemos añadir una entrada en el archivo tipo-estereotipo-defecto.xml como sigue:
<para estereotipo="REGIONES" tipo="String []"/>
Y definir nuestro editor en el editores.xml de nuestro proyecto:
<editor url="editorRegiones.jsp"> <!-- 1 -->
 <propiedad nombre="cantidadRegiones" valor="3"/> <!-- 2 -->
 <formateador clase="org.openxava.formatters.MultipleValuesByPassFormatter"/> <!-- 3 -->
 <para-estereotipo estereotipo="REGIONES"/>
</editor>
editorRegiones.jsp (1) es el archivo JSP que dibuja nuestro editor. Podemos definir propiedades que serán enviada al JSP como parámetros del request (2). El formateador tiene que implementar IMultipleValuesFormatter, que es similar a IFormatter pero usa String [] en vez de String. En este caso usamos un formateador genérico que simplemente deja pasar el dato.
Y para terminar escribimos nuestro editor JSP:
<%@ page import="java.util.Collection" %>
<%@ page import="java.util.Collections" %>
<%@ page import="java.util.Arrays" %>
<%@ page import="org.openxava.util.Labels" %>
 
<jsp:useBean id="style" class="org.openxava.web.style.Style" scope="request"/>
 
<%
String propertyKey = request.getParameter("propertyKey");
String [] fvalues = (String []) request.getAttribute(propertyKey + ".fvalue"); // (1)
boolean editable="true".equals(request.getParameter("editable"));
String disabled=editable?"":"disabled";
String script = request.getParameter("script");
boolean label = org.openxava.util.XavaPreferences.getInstance().isReadOnlyAsLabel();
if (editable || !label) {
 String sregionsCount = request.getParameter("cantidadRegiones");
 int regionsCount = sregionsCount == null?5:Integer.parseInt(sregionsCount);
 Collection regions = fvalues==null?Collections.EMPTY_LIST:Arrays.asList(fvalues);
%>
<select id="<%=propertyKey%>" name="<%=propertyKey%>" multiple="multiple"
 class=<%=style.getEditor()%>
 <%=disabled%>
 <%=script%>>
 <%
 for (int i=1; i<regionsCount+1; i++) {
 String selected = regions.contains(Integer.toString(i))?"selected":"";
 %>
 <option
 value="<%=i%>" <%=selected%>>
 <%=Labels.get("regions." + i, request.getLocale())%>
 </option>
 <%
 }
 %>
</select>
<%
}
else {
 for (int i=0; i<fvalues.length; i++) {
%>
<%=Labels.get("regions." + fvalues[i], request.getLocale())%>
<%
 }
}
%>
 
<%
if (!editable) {
 for (int i=0; i<fvalues.length; i++) {
%>
 <input type="hidden" name="<%=propertyKey%>" value="<%=fvalues[i]%>">
<%
 }
}
%>
Como se puede ver es como definir un editor para un valor simple, la principal diferencia es que el valor formateado (1) es un array de cadenas (String []) y no una cadena simple (String).
Como alternativa, puedes definir el editor anterior usando casillas de verificación (checkboxes) (nuevo en v4.9), como sigue:
<%@ page import="java.util.Collection" %>
<%@ page import="java.util.Collections" %>
<%@ page import="java.util.Arrays" %>
<%@ page import="org.openxava.util.Labels" %>
 
<jsp:useBean id="style" class="org.openxava.web.style.Style" scope="request"/>
 
<%
String propertyKey = request.getParameter("propertyKey");
String [] fvalues = (String []) request.getAttribute(propertyKey + ".fvalue");
boolean editable="true".equals(request.getParameter("editable"));
String disabled=editable?"":"disabled";
String script = request.getParameter("script");
boolean label = org.openxava.util.XavaPreferences.getInstance().isReadOnlyAsLabel();
if (editable || !label) {
    String sregionsCount = request.getParameter("cantidadRegiones");
    int regionsCount = sregionsCount == null?5:Integer.parseInt(sregionsCount);
    Collection regions = fvalues==null?Collections.EMPTY_LIST:Arrays.asList(fvalues);
    for (int i=1; i<regionsCount+1; i++) {
        String checked = regions.contains(Integer.toString(i))?"checked":"";
    %>
        <input name="<%=propertyKey%>" type="checkbox" class="<%=style.getEditor()%>"
                tabindex="1"
                value="<%=i%>"
                <%=checked%>
                <%=disabled%>
                <%=script%>
        />
        <%=Labels.get("regions." + i, request.getLocale())%>
    <%
    }
}
else {
    for (int i=0; i<fvalues.length; i++) {
%>
<%=Labels.get("regions." + fvalues[i], request.getLocale())%>
<%
    }
}
%>
 
<%
if (!editable) {
    for (int i=0; i<fvalues.length; i++) {
%>
        <input type="hidden" name="<%=propertyKey%>" value="<%=fvalues[i]%>">
<%
    }
}
%>

Editores para referencias (new in v3.1.3)

Por defecto las referencias se visualizan con una vista de detalle, pero podemos crear nuestro propio editor para las referencias. Por ejemplo, podemos escribir lo siguiente en el archivo editores.xml de nuestra aplicación:
<editor url="colorEditor.jsp">
 <para-referencia modelo="Color"/>
</editor>
 
Con este código estamos diciendo que cualquier referencia a la entidad Color se tiene que visualizar usando colorEditor.jsp (para usar un editor solo para una referencia concreta en una entidad concreta véase la sección Escoger un editor del capítulo 4).
Aquí tenemos el código para colorEditor.jsp:
<%@page import="java.util.Iterator"%>
<%@page import="org.openxava.test.model.Color"%><%
 
String propertyKey = request.getParameter("propertyKey"); // Id de la propiedad clave de la referencia (1)
Object value = request.getAttribute(propertyKey + ".value"); // Podemos usar propertyKey + ".value" (2)
if (value == null) value = 0;
 
for (Iterator it = Color.findAll().iterator(); it.hasNext(); ) {
 Color color = (Color) it.next();
 String checked = value.equals(color.getNumber())?"checked='checked'":"";
%>
<span style="font-weight: bold; color: #<%=color.getHexValue()%>; vertical-align: bottom">
 <input name="<%=propertyKey%>" value="<%=color.getNumber()%>" type="radio" <%=checked%>/> <!-- (3) -->
 <%=color.getName()%>
</span>
<%
}
%>
El parámetro "propertyKey" (1) nos da el id de la propiedad clave de la referencia. Podemos usarlo para nombrar el elemento HTML input (3) o para obtener su valor actual (2). La lista de parámetros que se puede usar es:
  1. referenceKey: El identificador único que OX da a esta referencia.
  2. propertyKey: El identificador único de la propiedad que es clave de la referencia.
  3. editable: Si la referencia tiene que ser editable por el usuario.
  4. viewObject: El nombre del objeto de sesión de la subvista que representa esta referencia. Solo aplica a editores compuestos.
  5. propertyPrefix: Prefijo usado para dar nombre a los editores para las propiedades. Solo aplica a editores compuestos.
Además, podemos definir la forma en que se visualizan todas la referencias por defecto para toda nuestra aplicación, usando <para-referencias/>. Para ello hemos de editar nuestro editores.xml y añadir:
<editor nombre="MiReferencia" url="miReferenciaEditor.jsp" enmarcable="true" compuesto="true">
 <para-referencias/>
</editor>
Ya que hemos marcado el editor con <para-referencias/> ahora todas las referencias de nuestra aplicación se visualizarán usando nuestro miReferenciaEditor.jsp. Esta es una forma sencilla de personalizar el comportamiento del generador de interfaz de usuario de OpenXava.

Editores para colecciones (nuevo en v3.1.3)

Por defecto las colecciones se visualizan con una lista de datos tabulares, pero podemos crear nuestro propio editor para colecciones. Por ejemplo, podemos escribir lo siguiente en el archivo editores.xml de nuestra aplicación:
<editor url="comentariosBlogEditor.jsp">
 <para-coleccion modelo="ComentarioBlog"/>
</editor>
Con el código de arriba estamos diciendo que cualquier colección de entidades ComentarioBlog tiene que ser visualizada y editada usando comentariosBlogEditor.jsp (para usar un editor solo para una colección concreta en una entidad concreta ver la sección Escoger un editor en el capítulo 4).
Aquí tenemos el código para comentariosBlogEditor.jsp:
<jsp:include page="collectionEditor.jsp">
 <jsp:param name="listEditor" value="comentariosBlogListEditor.jsp"/>
</jsp:include>
Esta es una forma habitual de crear un editor para colecciones. Aquí llamamos a collectionEditor.jsp (el editor por defecto de OpenXava para colecciones) enviando como argumento para listEditor un JSP que contiene el editor para parte de la lista. De esta forma tenemos gratis todas las acciones y comportamiento por defecto de las colecciones, por tanto solo hemos de preocuparnos por dibujar la lista.
El comentariosBlogListEditor.jsp es:
<%@ include file="../imports.jsp"%>
 
<%@page import="org.openxava.view.View"%>
<%@page import="org.openxava.model.MapFacade"%>
<%@page import="org.openxava.test.model.Blog"%>
<%@page import="org.openxava.test.model.BlogComment"%>
<%@page import="java.util.Iterator"%>
<%@page import="java.util.Map"%>
<%@page import="java.text.DateFormat"%>
<%@page import="org.openxava.util.Locales"%>
<%@page import="org.openxava.util.Is"%>
 
<jsp:useBean id="context" class="org.openxava.controller.ModuleContext" scope="session"/>
 
<%
String viewObject = request.getParameter("viewObject"); // Id para acceder al objeto view de la colección
View collectionView = (View) context.get(request, viewObject); // Obtenemos el objeto view de la colección mediante context
View rootView = collectionView.getRoot(); // En este caso usamos la vista raiz, la vista de Blog
Map key = rootView.getKeyValues();
if (Is.empty(key)) {
%>
No hay comentarios
<%
} else { // Si la clave tiene valor dibujamos la colección de comentarios
 
Blog blog = (Blog) MapFacade.findEntity("Blog", key);
String action = request.getParameter("rowAction"); // rowAction es la acción para editar o visualizar cada elemento
String actionArgv = ",viewObject=" + viewObject;
%>
 
Estos son los comentarios<br/>
<%
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, Locales.getCurrent());
int f=0;
for (Iterator it = blog.getComentarios().iterator(); it.hasNext(); f++) {
 ComentarioBlog comentario = (ComentarioBlog) it.next();
%>
<i><b><big>Comentario del <%=df.format(comentario.getFecha())%></big></b></i>
<xava:action action='<%=action%>' argv='<%="row=" + f + actionArgv%>'/>
<p>
<i><%=comentario.getCuerpo()%></i>
</p>
<hr/>
<%
}
 
}
%>
Este editor dibuja los comentarios del blog como un texto simple con una cabecera con la fecha.
La lista de parámetro a usar en un editor de lista para colecciones es:
  1. collectionName: El nombre de la colección tal y como lo tenemos en nuestra entidad.
  2. viewObject: El nombre del objeto de sesión de la subvista que representa esta colección.
  3. rowAction: El nombre calificado de acción (Controlador.accion como está en controladores.xml) a ejecutar en cada elemento para visualizarlo o editarlo.
Por supuesto, podemos crear nuestro editor para colección desde cero, sin usar collectionEditor.jsp. En este caso has de escribir la interfaz de usuario completa para la colección. Veamos un ejemplo en nombresTransportistaEditor.jsp:
<%@page import="org.openxava.view.View"%>
<%@page import="org.openxava.model.MapFacade"%>
<%@page import="org.openxava.test.model.Carrier"%>
<%@page import="java.util.Iterator"%>
 
<jsp:useBean id="context" class="org.openxava.controller.ModuleContext" scope="session"/>
 
<%
String viewObject = request.getParameter("viewObject"); // viewObject es el id del objeto view del padre
View view = (View) context.get(request, viewObject); // view es el objeto view de Transportista, el padre de la colección
Transportista transportista = (Transportista) MapFacade.findEntity("Transportista", view.getKeyValues());
%>
Los compañeros de <%=transportista.getNombre()%> son:<br>
<ul>
<%
for (Iterator it = transportista.getCompaneros().iterator(); it.hasNext(); ) {
 Transportista companero = (Transportista) it.next();
%>
<li><%=companero.getNombre()%></li>
<%
}
%>
</ul>
En este caso escribimos el código para dibujar la colección completamente, si queremos tener algunas acciones para trabajar con la colección tenemos que ponerlas nosotros mismos. ¡Ojo!, porque aquí viewObject es la vista del objeto que contiene la colección, no de la colección en sí.
La lista de parámetros a usar en un editor para colecciones es:
  1. collectionName: El nombre de la colección tal y como lo tenemos en nuestra entidad..
  2. viewObject: El nombre del objeto de sesión para la vista o subvista que representa el objeto padre de esta colección

Además, podemos definir la forma en que visualizan todas la colecciones en toda nuestra aplicación, usando <para-colecciones/>. Editemos nuestro editors.xml y añadamos:
<editor nombre="MiColeccion" url="miColeccionEditor.jsp">
 <para-colecciones/>
</editor>
Dado que hemos marcado el editor con <para-colecciones/> ahora todas las colecciones en nuestra aplicación se visualizan usando nuestro miColeccionEditor.jsp. Esta es una forma sencilla de personalizar el comportamiento del generador de interfaz gráfica de OpenXava.

Editores para tabs (modo lista) (nuevo en v4.6)

Por defecto los datos tabulares (los que se muestran en modo lista) se visualizan con una lista, pero podemos crear nuestro propio editor. Por ejemplo, podemos escribir esto en el editores.xml de nuestra aplicación:
<editor url="empleadoCorporativoListEditor.jsp">
  <para-tab modelo="EmpleadoCorporativo"/>
</editor>
Con el código de arriba estamos diciendo que todos los tabs (es decir todas la listas) para la entidad EmpleadoCorporativo tienen que ser editados y visualizados usando empleadoCorporativoListEditor.jsp (para usar un editor sólo para un tab concreto de una entidad ver la sección Escogiendo un editor en el capítulo 5).
Aqui tenemos el código para empleadoCorporativoListEditor.jsp:
<%@ include file="../imports.jsp"%>
 
<jsp:useBean id="context" class="org.openxava.controller.ModuleContext" scope="session"/>
 
<%
String tabObject = request.getParameter("tabObject");
tabObject = (tabObject == null || tabObject.equals(""))?"xava_tab":tabObject;
org.openxava.tab.Tab tab = (org.openxava.tab.Tab) context.get(request, tabObject);
String condition = tab.getBaseCondition()==null?"":tab.getBaseCondition();
String all = condition.equals("")?"selected":"";
String low = condition.contains("<=")?"selected":"";
String high = condition.contains(">")?"selected":"";
String action="openxava.executeAction('OpenXavaTest', 'EmpleadoCorporativo'," +
    "false, false, 'EmpleadoCorporativo.filtrar', 'segmento='+this.value)";
%>
 
<select name="<xava:id name='escogerSegmento'/>" style='margin-bottom: 6px' onchange=
    "<%=action%>">
    <option value="all" <%=todos%>>Todos los empleados</option>
    <option value="low" <%=bajo%>>Empleados con salario bajo</option>
    <option value="high" <%=alto%>>Empleados con salario alto</option>
</select>
 
<jsp:include page="listEditor.jsp"/>
Un detalle importante es que este editor incluye listEditor.jsp al final. listEditor.jsp es el editor por defecto para modo lista, por tanto en este caso simplemente estamos refinando la lista estándar añadiendo un combo para escoger un filtro personalizado. Sin embargo, podemos crear nuestro propio editor para lista desde cero, por ejemplo, el siguiente editor, fichasClienteListEditor.jsp, muestra la lista de clientes como una fila de fichas:
<%@ include file="../imports.jsp"%>
 
<jsp:useBean id="context" class="org.openxava.controller.ModuleContext" scope="session"/>
 
<%
String collection = request.getParameter("collection");
String id = "list";
String collectionArgv = "";
String prefix = "";
String tabObject = request.getParameter("tabObject");
tabObject = (tabObject == null || tabObject.equals(""))?"xava_tab":tabObject;
if (collection != null && !collection.equals("")) {
    id = collection;
    collectionArgv=",collection="+collection;
    prefix = tabObject + "_";
}
org.openxava.tab.Tab tab = (org.openxava.tab.Tab) context.get(request, tabObject);
org.openxava.tab.impl.IXTableModel model = tab.getTableModel();
for (int r=tab.getInitialIndex(); r<model.getRowCount() && r < tab.getFinalIndex(); r++) {
%>
    <xava:link action="List.viewDetail"><div
      style="border: 2px solid rgb(130, 143, 149); display: inline-block; padding: 10px; margin-bottom: 10px;">
    <h4><%=model.getValueAt(r, 1)%>(<%=model.getValueAt(r, 0)%>)</h4>
    <%=model.getValueAt(r, 2)%><br/>
    <%=model.getValueAt(r, 3)%> (<%=model.getValueAt(r, 4)%>)
    </div></xava:link>
<%
}
%>
Además, podemos definir la forma en que las listas se visualizan por defecto en toda la aplicación, usando <para-tabs/>, para ello editamos editores.xml y añadimos:
<editor nombre="MiLista" url="miListaEditor.jsp">
  <para-tabs/>
</editor>
Como hemos marcado el editor con <for-tabs/> ahora todos los tabs de nuestra aplicación se visualizaran usando nuestro miListaEditor.jsp. Esta es una forma sencilla de personalizar el comportamiento del generador de interfaz gráfica de OpenXava.

JavaScript en los editores

A partir de v4m3

Si necesitas usar funciones JavaScript, ya sean propias o de terceros, en tu editor, no lo puedes hacer incluyendolas directamente en el JSP, porque el código HTML del editor se carga vía AJAX. En lugar de eso, has de poner tus funciones en un archivo JS en la carpeta web/xava/editors/js de tu proyecto. Todo el JavaScript que hay ahí se carga automáticamente.
Es decir, si necesitas usar una función JavaScript f() en tu editor, como sigue:
<input ... onclick="f()"/>
Has de definir la función f() en una archivo JS. Crea un archivo llamado miEditor.js (o cualquier otro nombre que quieras), y ponlo en la carpeta web/xava/editors/js. El contenido de ese archivo puede ser:
function f() {
 alert("Hola. Soy f()");
}
Por otra parte. Si necesitas lógica JavaScript para inicializar tu editor, no puedes usar el evento onload o equivalente, porque el código HTML del editor es cargado vía AJAX, por tanto no se produce carga de la página. Has de registrar tu código de inicialización en OpenXava. Puedes hacerlo en tu archivo miEditor.js (o cualquier otro archivo en web/xava/editors/js), como sigue:
openxava.addEditorInitFunction(function() {
 /*
 Aquí tu código de inicialización para tu editor.
 Es decir, las cosas que normalmente pondrías en el event onload de JavaScript
 o $(function() { ... }) de jQuery
 */
 ...
});
Vemos como usamos openxava.addEditorInitFunction() para registrar una función de inicialización. El evento JavaScript onload o el evento ready() de jQuery no funciona, porque no se produce carga de página, en vez de eso el editor se genera en el servidor, se carga vía AJAX y se inserta en la página que ya se estaba visualizando.
A partir de v4.8.1 puedes definir una función de destrucción para tu editor en tu miEditor.js:
openxava.addEditorDestroyFunction(function() { // Nuevo en v4.8.1
/*
Aquí el código de destrucción de tu editor.
Esto es para liberar los recursos obtenidos por el editor.
*/
...
});

Hasta v4m2

Has de poner todo el código JavaScript para todos tus editores en custom-editors.js en la carpeta web/xava/js. Esta técnica todavía se soporta, aunque se considera obsoleta.

Until 3.0.x

No se usa AJAX, por tanto el código JavaScript se puede incluir directamente en el JSP del editor.

Editores personalizables y estereotipos para crear combos

Podemos hacer que propiedades simples que se visualicen como combos que rellenen sus datos desde la base datos. Veámoslo.
Definimos las propiedades así en nuestro componente:
@Stereotype("FAMILY")
private int familyNumber;
 
@Stereotype("SUBFAMILY")
private int subfamilyNumber;
Y en nuestro editores.xml ponemos:
<editor url="descriptionsEditor.jsp"> <!-- 10 -->
 <propiedad nombre="modelo" valor="Familia"/> <!-- 1 -->
 <propiedad nombre="propiedadClave" valor="codigo"/> <!-- 2 -->
 <propiedad nombre="propiedadDescripcion" valor="descripcion"/> <!-- 3 -->
 <propiedad nombre="ordenadoPorClave" valor="true"/> <!-- 4 -->
 <propiedad nombre="readOnlyAsLabel" valor="true"/> <!-- 5 -->
 <para-estereotipo estereotipo="FAMILIA"/> <!-- 11 -->
</editor>
 
<!-- Es posible especificar dependencias de estereotipos o propiedades -->
<editor url="descriptionsEditor.jsp" <!-- 10 -->
 depende-de-estereotipos="FAMILIA"> <!-- 12 -->
<!--
<editor url="descriptionsEditor.jsp" depende-de-propiedades="codigoFamilia"> <!-- 13 -->
-->
 <propiedad nombre="modelo" valor="Subfamilia"/> <!-- 1 -->
 <propiedad nombre="propiedadClave" valor="codigo"/> <!-- 2 -->
 <propiedad nombre="propiedadesDescripcion" valor="codigo, descripcion"/> <!-- 3 -->
 <propiedad nombre="condicion" value="${codigoFamilia} = ?"/> <!-- 6 -->
 <propiedad nombre="estereotiposValoresParametros" valor="FAMILIA"/> <!-- 7 -->
 <!--
 <propiedad nombre="propiedadesValoresParametros" valor="codigoFamilia"/> <!-- 8 -->
 -->
 <propiedad nombre="formateadorDescripciones" <!-- 9 -->
 valor="org.openxava.test.formatters.FormateadorDescripcionesFamilia"/>
 <para-estereotipo estereotipo="SUBFAMILIA"/> <!-- 11 -->
</editor>
Al visualizar una vista con estas dos propiedades codigoFamilia y codigoSubfamilia sacará un combo para cada una de ellas, el de familias con todas las familias disponible y el de subfamilias estará vacío y al escoger una familia se rellenará con sus subfamilias correspondientes.
Para hacer eso asignamos a los estereotipos (FAMILIA y SUBFAMILIA en este caso(11)) el editor descriptionsEditor.jsp (10) y lo configuramos asignandole valores a sus propiedades. Algunas propiedades con las que podemos configurar estos editores son:
  1. modelo: Modelo del que se obtiene los datos. Puede ser el nombre de una entidad (Factura) o el nombre de un modelo usado en una colección incrustada (Factura.LineaFactura).
  2. propiedadClave o propiedadesClave: Propiedad clave o lista de propiedades clave que es lo que se va a usar para asignar valor a la propiedad actual. No es obligado que sean las propiedades clave del modelo, aunque sí que suele ser así.
  3. propiedadDescripcion o propiedadesDescripcion: Propiedad o lista de propiedades a visualizar en el combo.
  4. ordenadoPorClave: Si ha de estar ordenador por clave, por defecto sale ordenado por descripción. También se puede usar order con un orden al estilo SQL, si lo necesitas.
  5. readOnlyAsLabel: Si cuando es de solo lectura se ha de visualizar como una etiqueta. Por defecto es false.
  6. condicion: Condición para restringir los datos a obtener. Tiene formato SQL, pero podemos poner nombres de propiedades con ${}, incluso calificadas. Podemos poner argumentos con ?. En ese caso es cuando dependemos de otras propiedades y solo se obtienen los datos cuando estas propiedades cambian.
  7. estereotiposValoresParametros: Lista de estereotipos de cuyas propiedades dependemos. Sirven para rellenar los argumentos de la condición y deben coincidir con el atributo depende-de-estereotipos. (12)
  8. propiedadesValoresParametros: Lista de propiedades de las que dependemos. Sirven para rellenar los argumentos de la condición y deben coincidir con el atributo depende-de-propiedades. (13)
  9. formateadorDescripciones: Formateador para las descripciones visualizadas en el combo. Ha de implementar IFormatter.
Siguiendo este ejemplo podemos hacer fácilmente nuestro propios estereotipos que visualicen una propiedad simple con un combo con datos dinámicos. Sin embargo, en la mayoría de los casos es más conveniente usar referencias visualizadas como @DescriptionsList; pero siempre tenemos la opción de los estereotipos disponible.

Vistas JSP propias y taglibs de OpenXava

Obviamente la mejor forma de crear interfaces de usuario es usando las anotaciones de vista que se ven en el capítulo 4. Pero, en casos extremos quizás necesitemos definir nuestra propia vista usando JSP. OpenXava nos permite hacerlo. Y para hacer más fácil la labor podemos usar algunas taglibs JSP provistas por OpenXava. Veamos un ejemplo.

Ejemplo

Lo primero es indicar en nuestro módulo que queremos usar nuestro propio JSP, en aplicacion.xml:
<modulo nombre="ComercialJSP" carpeta="facturacion.variaciones">
 <modelo nombre="Comercial"/>
 <vista nombre="ParaJSPPropio"/> <!-- 1 -->
 <vista-web url="jsp-propios/comercial.jsp"/> <!-- 2 -->
 <controlador nombre="Typical"/>
</modulo>
Si usamos vista-web (2) al definir el módulo, OpenXava usa nuestro JSP para dibujar el detalle, en vez de usar la vista generada automáticamente. Opcionalmente podemos definir una vista OpenXava con vista (1), esta vista es usada para saber que eventos lanzar y que propiedades llenar, si no se especifica se usa la vista por defecto de la entidad; aunque es aconsejable crear una vista OpenXava explícita para nuestra vista JSP, de esta manera podemos controlar los eventos, las propiedades a rellenar, el orden del foco, etc explicitamente. Podemos poner nuestro JSP dentro de la carpeta web/jsp-propios (u otra de nuestra elección) de nuestro proyecto, y este JSP puede ser así:
<%@ include file="../xava/imports.jsp"%>
 
<table>
<tr>
 <td>C&oacute;digo: </td>
 <td>
 <xava:editor property="codigo"/>
 </td>
</tr>
<tr>
 <td>Nombre: </td>
 <td>
 <xava:editor property="nombre"/>
 </td>
</tr>
 
<tr>
 <td>Nivel: </td>
 <td>
 <xava:editor property="nivel.id"/>
 <xava:editor property="nivel.descripcion"/>
 </td>
</tr>
</table>
Somos libres de crear el archivo JSP como queramos, pero puede ser práctico usar las taglibs de OpenXava, en este caso, por ejemplo, se usa <xava:editor/>, esto dibuja un editor apto para la propiedad indicada, además añade el JavaScript necesario para lanzar los eventos. Si usamos <xava:editor/>, podemos manejar la información visualizada usando el objeto xava_view (del tipo org.openxava.view.View), por lo tanto todos los controladores estándar de OpenXava (CRUD incluido) funcionan.
Podemos observar como las propiedades cualificadas están soportadas (como nivel.id o nivel.descripcion) (nuevo en v2.0.1), además cuando el usuario rellena nivel.id, nivel.descripcion se llena con su valor correspondiente. Sí, todo el comportamiento de una vista OpenXava está disponible dentro de nuestros JSPs si usamos las taglibs de OpenXava.
Veamos las taglib de OpenXava.

xava:editor

La marca (tag) <xava:editor/> permite visualizar un editor (un control HTML) para nuestra propiedad, de la misma forma que lo hace OpenXava cuando genera automáticamente la interfaz de usuario.
<xava:editor
 property="nombrePropiedad" <!-- 1 -->
 editable="true|false" <!-- 2 Nuevo en v2.0.1 -->
 throwPropertyChanged="true|false" <!-- 3 Nuevo en v2.0.1 -->
/>
  1. property (obligado): Propiedad del modelo asociado al módulo actual.
  2. editable (opcional): Nuevo en v2.0.1. Fuerza a este editor a ser editable, de otra forma se asume un valor por defecto apropiado.
  3. throwPropertyChanged (opcional): Nuevo en v2.0.1. Fuerza a este editor a lanzar el evento de propiedad cambiada, de otra forma se asume un valor por defecto apropiado.
Esta marca genera el JavaScript para permitir a nuestra vista trabajar de la misma forma que una vista automática. Las propiedades calificadas (propiedades de referencias) están soportadas (nuevo en v2.0.1).

xava:action, xava:link, xava:image, xava:button

La marca (tag) <xava:action/> permite dibujar una acción (un botón o una imagen que el usuario puede pulsar).
<xava:action action="controlador.accion" argv="argv"/>
El atributo action indica la acción a ejecutar, y el atributo argv (opcional) nos permite establecer valores a algunas propiedades de la acción antes de ejecutarla. Un ejemplo:
<xava:action action="CRUD.save" argv="resetAfter=true"/>
Cuando el usuario pulse en la acción se ejecutará CRUD.save, antes pone a true la propiedad resetAfter de la acción.
La acción se visualiza como una imagen si tiene una imagen asociada y como un botón si no tiene imagen asociada. Si queremos detereminar el estilo de visualización podemos usar directamente las siguientes marcas: <xava:button/>, <xava:image/> o <xava:link/> similares a <xava:action/>.
Podemos especificar una cadena vacía para la acción (nuevo en v2.2.1), como sigue:
<xava:action action=""/>
En este caso la marca (tag) no tiene efecto y no se produce error. Esta característica puede ser útil cuando el nombre de la acción lo obtenemos dinámicamente (es decir action=”<%=micodigo()%>”), y el valor pueda estar vacío en ciertos casos.

xava:message (nuevo en v2.0.3)

La marca (tag) <xava:message/> permite mostrar en HTML un mensaje de los archivos de recursos i18n de OpenXava.
<xava:message key="clave_mensaje" param="parametroMensaje" intParam="paramMensaje"/>
El mensaje es buscado primero en los archivos de recursos de nuestro proyecto (MiProyecto/i18n/MensajesMiProyecto.properties) y si no se encuentra ahí es buscado en los mensajes por defecto de OpenXava (OpenXava/i18n/Messages.properties).
Los atributos param y intParam son opcionales. El atributo intParam es usado cuando el valor a enviar como parametro es de tipo int. Si usamos Java 5 podemos usar siempre param porque int es automáticamente convertido por autoboxing.
Esta marca solo genera el texto del mensaje, sin ningun tipo de formateo HTML.
Un ejemplo:
<xava:message key="cantidad_lista" intParam="<%=cantidadTotal%>"/>

xava:descriptionsList (nuevo en v2.0.3)

La marca (tab) <xava:descriptionsList/> permite visualizar una lista descripciones (un combo HTML) para una referencia, del mismo modo que lo hace OpenXava cuando genera la interfaz de usuario automáticamente.
<xava:descriptionsList
 reference="nombreReferencia" <!-- 1 -->
/>
  1. reference (obligado): Una referencia del modelo asociado con el módulo actual.
Esta marca genera el JavaScript necesario para permitir a la vista personalizada trabajar de la misma forma que una automática.
Un ejemplo:
<tr>
 <td>Nivel: </td>
 <td>
 <xava:descriptionsList reference="nivel"/>
 </td>
</tr>
En este caso nivel es una referencia al modelo actual (por ejemplo Comercial). Un combo es mostrado con todos los niveles disponibles.

Configuración de propiedades en xava.properties

El archivo xava.properties permite cambiar el comportamiento de OpenXava para toda la aplicación.

Propiedad
Descripción
Valor por defecto
Correo Electronico
emailAsUserNameInPortal

false
smtpHost
Servidor de correo SMTP

smtpHostTrusted (nuevo en v4.7)
Si es true se puede utilizar un servidor de correos SMTP con el certificado vencido
false
smtpPort
Puerto para envio de correos

smtpUserId
Usuario para conectar al servidor SMTP

smtpUserPassword
Contraseña del usuario para servicios de correo


Persistencia
defaultPersistenceUnit
Unidad de persistencia predeterminada
default
jpaCodeInPOJOs

Depends on persistence provider
mapFacadeAsEJB

false
mapFacadeAutoCommit

false
persistenceProviderClass
Clase encargada de manejar la persistencia de datos
org.openxava.model.impl.JPAPersistenceProvider

Etiquetas, Mensajes y Localización
i18nWarnings

false
portletLocales
Si no se especifica se usan los locales incluidos en la carpeta i18n
bg, ca, de, en, es, fr, in, it, ja, ko, nl, pl, pt, ru, sv, zh

Aplicación y Controladores
defaultLabelFormat
Possibles values for defaultLabelFormat are: NORMAL, SMALL and NO_LABEL
NORMAL
defaultLabelStyle
Se han definido: bold-label, italic-label, además de que puede definir sus propios estilos

defaultModeController (nuevo en v4m5)
Los valores posibles para defaultModeController son: Mode, DetailList, DetailOnly, ListOnly and SplitOnly
Mode
duplicateComponentWarnings
Cuando se encuentran componentes con nombres duplicados se generan mensajes de advertencia
true
failOnAnnotationMisuse
Lanza un error si algún miembro (propiedades, referencias o colecciones) tienen anotaciones que no les son aplicables
true
generateDefaultModules
Si es true no es requerido definir los módulos en application.xml / applicacion.xml. OX se encarga de generarlos automaticamente
true

Estilos
liferay6StyleClass (nuevo en v4m6)
Estilo compatible con liferay 6
org.openxava.web.style.Liferay6Style
liferay51StyleClass
Estilo compatible con liferay 5.1
org.openxava.web.style.Liferay51Style
liferay41StyleClass
Estilo compatible con liferay 4.1
org.openxava.web.style.Liferay41Style
liferay43StyleClass
Estilo compatible con liferay 4.3
org.openxava.web.style.Liferay43Style
styleClass
Maneja la asignación de atributos class para los elementos desplegables. Provee un simple y efectivo mecanismo para hacer OX compatible con la interfaces de los portales
org.openxava.web.style.Liferay51Style
styleCSS
URL del archivo CSS que provee las caracteristicas de la interfase visual a ser utilizada cuando la applicación es ejecutada fuera de portales
liferay51/css/everything_unpacked.css
webSpherePortal61StyleClass
Estilo compatible WebSphere Portal 6.1
org.openxava.web.style.WebSpherePortal61Style

Vistas
alignedByColumns (nuevo en v4.7.1)
Si es true las propiedades de la vista son desplegadas alineadas por columnas. Sólo funciona en las implementaciones por defecto de layoutParser & layoutPainter. Provoca que las todas las vistas se comporten como si tuviesen # al inicio
false
buttonsForNoImageActions
Si es true, cuando una acción no tiene imagen asociada muestra un botón. Si es false entonces muestra un hipervínculo
false
layoutParser (nuevo en v4.5)
Nombre del interpretador de despliegue de vistas, la implementación de OX es org.openxava.web.layout.impl.DefaultLayoutParser (Beta)

layoutPainter (nuevo en v4.5)
Nombre de la clase encargada de dibujar las vistas, la implementación de OX es: org.openxava.web.layout.impl.DefaultLayoutPainter (Beta)

maxSizeForTextEditor
Limita el tamaño del campo de entrada para propiedades que representen textos largos
100
messagesOnTop (nuevo en v4.5)
Si es true los errores, advertencias y mensajes son mostrados en la parte superior de la página, si es false son mostrados al pie de la página
true
readOnlyAsLabel

false
showIconForViewReadOnly (nuevo en v4.6)


showLabelsForToolBarActions (nuevo en v4m6)
Si es false, la barra de botones no despliega lo nombre de las acciones, sólo las imágenes
true

Listas y Colecciones
addColumnsPageRowCount
Limita el numero de propiedades seleccionables para ser adicionadas a las columnas de las listas o propiedades
100
customizeList (nuevo en v4m5)
Si es false, no se permite personalizar las columnas de la lista
true
detailOnBottomInCollections

false
ignoreAccentsForStringArgumentsInConditions
(nuevo en v4m6)
Si es true Ignora las acentuaciones en las condiciones de lista y colecciones
false
pageRowCount
Default number of objects to show in lists and collections
10
resizeColumns (nuevo en v4m5)
Si es false, No se permite cambiar el tamaño de las columnas
true
saveAndStayForCollections (nuevo en v4m6)
Si es false, el boton grabar y continuar no es mostrado cuando adicionan elementos a collecciones
true
showCountInList

true
showIconForViewReadOnly

true
showFilterByDefaultInList
Si es true, la fila de filtrar se muestra inicialmente en las listas, El usuario siempre tiene la opción de mostrar / ocultar la fila de filtrado
true
showFilterByDefaultInCollections
Si es true,
la fila de filtrar se muestra inicialmente en las colecciones, El usuario siempre tiene la opción de mostrar / ocultar la fila de filtrado
true
summationInList (nuevo en v4.3)
Si es true Se muestra una fila de totales al final de la lista, bajo los campos numericos. El usuario puede manualmente encender o apagar los totales para cada columna
true
tabAsEJB

false
toUpperForStringArgumentsInConditions
Si es true se convierten a mayusculas los argumentos de busqueda en las listas y colecciones. Aunque esto permite las busquedas de datos independientemente de que esten en mayusculas o minusculas, puede producir bajos rendimientos con algunos manejadores de base de datos
true
filterOnChange (nuevo en v4.8)
El filtrado se realiza automáticamente al seleccionar una opción de un combo, sin pulsar en el botón de filtrar
true

Ayuda
helpInNewWindow (nuevo en v4m5)
Si es true la página de ayuda se muestra en una nueva ventana, si es false, la página de ayuda es abierta en la ventana actual
true
helpPrefix (nuevo en v4m5)
Prefijo para la generación del URL de ayuda

helpSuffix (nuevo en v4m5)
Sufijo para la generación del URL de ayuda


Reportes
reportParametersProviderClass
Clase para obtener los parametros para los informes
org.openxava.util.DefaultReportParametersProvider

Miscelaneos
csvEncoding (nuevo en v4.2.1)
Porque es imposible obtener la codificación del cliente, que es usada por Excel al abrir el archivo. UTF-8, ISO-8859-1

csvSeparator

;
hibernateJavaLoggingLevel
Nivel para la bitácora de Hibernate. Los valores pueden ser: SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL, OFF
INFO
javaLoggingLevel
Nivel para la bitácora de Java. Los valores válidos son: SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL, OFF
INFO