Reference guide:
Model
|
View |
Tabular
data |
Object/relational
mapping |
Controllers
|
Application |
Customizing
Tabular data is data that is displayed in table format. If you create
a conventional OpenXava module, then the user can manage the component
data with a list like this:

This list allows user to:
- Filter by any columns or a combination of them.
- Order by any column with a single click.
- Display data by pages, and therefore the user can work efficiently
with millions of records.
- Customize the list: add, remove and change the column order (with
the little pencil in the left top corner). This customizations are
remembered by user.
- Generic actions to process the objects in the list: generate PDF
reports, export to Excel or remove the selected objects.
The default list is enough for many cases, moreover the user can customize
it. Nevertheless, sometimes it is convenient to modify the list behavior.
For this you have the
@Tab annotation within the entity
definition.
The syntax of
@Tab is:
@Tab(
name="name", // 1
filter=filter class, // 2
rowStyles=array of @RowStyle, // 3
properties="properties", // 4
baseCondition="base condition", // 5
defaultOrder="default order", // 6
editor="defult editor", // 7 New in v4.6
editors="all available editors" // 8 New in v5.7
)
public class MyEntity {
- name (optional): You can define several tabs in a
entity (use @Tabsfor versions older than 6.1), and set
a name for each one. This name is used to indicate the tab that you
want to use (usually in application.xml).
- filter
(optional): Allows to define programmatically some logic to apply to
the values entered by user when he filters the list data.
- rowStyles
(optional): A simple way to specify a different visual style for some
rows. Normally to emphasize rows that fulfill certain condition. You
specify an array of @RowStyle, in this way you can use several
styles for a tab.
- properties
(optional): The list of properties to show initially. Can be qualified
(that is you can specify referenceName.propertyName at any
depth level). The suffix + (new in v4.1) can be added to a
property to show the sum
of the column at bottom.
- baseCondition
(optional): Condition to be fulfilled by the displayed data. It's
added to the user condition if needed.
- defaultOrder
(optional): To specify the initial order for data.
- editor
(optional): (New in v4.6) Editor from default-editors.xml
or editors.xml used to display the list. It's used for the
default format, if the list has several formats the other formats are
not affected.
- editores
(optional): (New in v5.7) Comma separated list of editors
used to display the list. Each editor corresponds to an available
format for the users. The editors are declared in default-editors.xml
or editors.xml.
Initial
properties and emphasize rows
The most simple customization is to indicate the properties to show
initially:
@Tab(
rowStyles=@RowStyle(style="row-highlight", property="type", value="steady"),
properties="name, type, seller.name, address.city, seller.level.description, address.state.name"
)
These properties are shown the first time the module is executed, after
that the user will have the option to change the properties to display.
Also you see how you can use qualified properties (properties of
references) in any level.
In this case you can see also how to indicate a
@RowStyle; you are saying that the object
which property
type has the value
steady will use the
style
row-highlight. The style has to be defined in the CSS
style-sheet. The
row-highlight (
highlight in versions
previous to v4m3) style is already defined in OpenXava, but you can define
your own styles with the
custom.css file in
web/xava/style
(new in v4.5).
The visual effect of above is:
Filters
and base condition
A common technique is to combine a filter with a base condition:
@Tab(name="Current",
filter=CurrentYearFilter.class,
properties="year, number, amountsSum, vat, detailsCount, paid, customer.name",
baseCondition="${year} = ?"
)
The condition has to have SQL syntax, you can use
? for
arguments and the property names inside
${}. In this case a
filter is used to set the value of the argument. The filter code is:
package org.openxava.test.filters;
import java.util.*;
import org.openxava.filters.*;
/**
* @author Javier Paniza
*/
public class CurrentYearFilter implements IFilter { // (1)
public Object filter(Object o) throws FilterException { // (2)
Calendar cal = Calendar.getInstance();
cal.setTime(new java.util.Date());
Integer year = new Integer(cal.get(Calendar.YEAR));
Object [] r = null;
if (o == null) { // (3)
r = new Object[1];
r[0] = year;
}
else if (o instanceof Object []) { // (4)
Object [] a = (Object []) o;
r = new Object[a.length + 1];
r[0] = year;
for (int i = 0; i < a.length; i++) {
r[i+1]=a[i];
}
}
else { // (5)
r = new Object[2];
r[0] = year;
r[1] = o;
}
return r;
}
}
A filter gets the arguments of user type for filtering in lists and for
processing, it returns the value that is sent to OpenXava to execute the
query. As you see it must implement
IFilter (1), this force it
to have a method named
filter (2) that receives a object with
the value of arguments and returns the filtered value that will be used as
query argument. These arguments can be null (3), if the user does not type
values, a simple object (5), if the user types a single value or an object
array (4), if the user types several values. The filter must consider all
cases. The filter of this example adds the current year as first argument,
and this value is used for filling the arguments in the
baseCondition
of the tab.
To sum up, the tab that you see above only shows the invoices of the
current year.
Another case:
@Tab(name="DefaultYear",
filter=DefaultYearFilter.class,
properties="year, number, customer.number, customer.name, amountsSum, " +
"vat, detailsCount, paid, importance",
baseCondition="${year} = ?"
)
In this case the filter is:
package org.openxava.test.filters;
import java.util.*;
import org.openxava.filters.*;
/**
* @author Javier Paniza
*/
public class DefaultYearFilter extends BaseContextFilter { // (1)
public Object filter(Object o) throws FilterException {
if (o == null) {
return new Object [] { getDefaultYear() }; // (2)
}
if (o instanceof Object []) {
List c = new ArrayList(Arrays.asList((Object []) o));
c.add(0, getDefaultYear()); // (2)
return c.toArray();
}
else {
return new Object [] { getDefaultYear(), o }; // (2)
}
}
private Integer getDefaultYear() throws FilterException {
try {
return getInteger("xavatest_defaultYear"); // (3)
}
catch (Exception ex) {
ex.printStackTrace();
throw new FilterException(
"Impossible to obtain default year associated with the session");
}
}
}
This filter extends
BaseContextFilter, this allow you to access
to the session objects of OpenXava. You can see how it uses a method
getDefaultYear()
(2) that call to
getInteger() (3) which (as
getString(),
getLong() or the more generic
get()) that allows you to
access to value of the session object
xavatest_defaultYear. This
object is defined in
controllers.xml this way:
<object name="xavatest_defaultYear" class="java.lang.Integer" value="1999"/>
The actions can modify it and its life is the user session life but it's
private for each module. This issue is treated in more detail in
chapter 7.
This is a good technique for data shown in list mode to depend on the user
or the configuration that he has chosen.
Also it's possible to access environment variables inside a filter of type
BaseContextFilter, using
getEnvironment() method, just
in this way:
new Integer(getEnvironment().getValue("XAVATEST_DEFAULT_YEAR"));
For learning more about environment variables see the
chapter 7 about controllers.
Partial
select (new in v5.6)
In
baseCondition you can write the select statement from the
FROM clause, to do that just start the condition with
from:
@Tab(name="FromAlaska",
baseCondition="from Customer e, in (e.states) s where s.id = 'AK'")
Use the JPQL syntax with
e as alias for the main entity.
This is better option than using the complete select because the list of
properties is generated by OpenXava, so the user can customize the list
while the developer still has the option of creating sophisticated
queries.
Complete select
You can write the complete select statement to obtain the tab data. Since
v4.5 you have to use JPQL syntax:
@Tab(name="CompleteSelect",
properties="number, description, family",
baseCondition =
"select e.number, e.description, f.description " +
"from Subfamily e, Family f " +
"where e.familyNumber = f.number"
)
The current implementation requires using
e as alias for the
main entity.
Until v4.4.x it used SQL syntax:
@Tab(name="CompleteSelect",
properties="number, description, family",
baseCondition=
"select" +
" ${number}, ${description}, XAVATEST.FAMILY.DESCRIPTION " +
"from " +
" XAVATEST.SUBFAMILY, XAVATEST.FAMILY " +
"where " +
" XAVATEST.SUBFAMILY.FAMILY = " +
" XAVATEST.FAMILY.NUMBER"
)
Use it only in extreme cases. Normally it is not necessary, and if you use
this technique the user cannot customize his list.
Default order
Finally, setting a default order is very easy:
@Tab(name="Simple", properties="year, number, date",
defaultOrder="${year} desc, ${number} desc"
)
This specified the initial order and the user can choose any other order
by clicking in the heading of a column.
Default values for tabs at
application level (new in v4m4)
You can define default values for all (or selected)
@Tabs of
your application at once. To do so, create a
tabs-default-values.xml
file in the
xava folder of your application, just as the next
example:
<?xml version = "1.0" encoding = "ISO-8859-1"?>
<!DOCTYPE tabs-default-values SYSTEM "dtds/tabs-default-values.dtd">
<tabs-default-values>
<tab>
<filter class="org.openxava.test.filters.ActiveYearFilter"/>
<base-condition>${year} = ?</base-condition>
<for-model model="Delivery"/>
<for-model model="Invoice"/>
</tab>
<tab>
<properties>year, number, date</properties>
<default-order>${number} desc</default-order>
<for-model model="Delivery"/>
</tab>
<tab>
<filter class="org.openxava.filters.UserFilter"/>
<base-condition>${user} = ?</base-condition>
<except-for-model model="User"/>
</tab>
<tab>
<default-order>${oid} asc</default-order>
<for-all-models/>
</tab>
</tabs-default-values>
The
<tab/> element follows the syntax of
XML Components for tabs. With the addition of
<for-model/>,
<except-for-model/> and
<for-all-models/>
used to apply the values to the tabs of the desired entities.
With this
tab elements you define
default values for
the tabs of your entities, therefore the values used in the
@Tabs
defined in your entities always take preference over these ones.
Column
summation (new in v4.1)
To show the sum of all the value of a column at the bottom of the list you
only have to add the + symbol to the property name, as following:
@Tab( properties = "year, number, description, amount+" )
In this case the sum of the
amount column will be shown just as
in the next figure:

The summation is only allowed for not calculated numeric properties.
Choosing
an editor (new in v4.6)
An editor is the actual code (usually a JSP) that displays the list to the
user. By default, the editor OpenXava uses for displaying tabular data is
a list with pagination, filtering, ordering and search, but you can
specify your own editor to display a concrete tab using the atribute
editor
in
@Tab.
For example, if you have a list of a
Customer entities and you
want to display it using a custom user interface, such as a row of cards,
you can do it in this way:
@Tab ( name ="Cards", editor="CustomerCardList",
properties="number, name, type, address.city, address.state.name"
)
In this case the
CustomerCardList editor will be used for
displaying/editing the tab data, instead of the default one. You must
define your
CustomerCardList editor in the
xava/editors.xml
file of your project:
<editor name="CustomerCardList" url="customerCardListEditor.jsp"/>
Also you have to write the JSP code for your editor in
customerCardListEditor.jsp.
This feature is for changing the editor for a concrete tab in a concrete
entity. If you want to change the editor for all tabs of a certain entity
type at application level then it's better to configure it using
xava/editors.xml
file.
Learn more on
Editors
for tabs section of
chapter
9.
Disabling
customization
The user can customize the list adding, moving, removing columns and some
more things:

If you do not want that your users customizing the list you can disable it
at application level adding the next entry in
xava.properties:
customizeList=false
If you want to disable the list customization for a specific list under
certain circumstances you can do it by code:
public class MyAction extends TabBaseAction {
public void execute() throws Exception {
if (myCondition) {
getTab().setCustomizeAllowed(false);
}
...
}
}
If you want to disable the customization for just a module, there is
already a controller for that, called
NoCustomizeList (new in v5.0).
Use it when you define your module in
application.xml (look at
chapter 8) as
following:
<module name="Warehouse">
<model name="Warehouse"/>
<controller name="Typical"/>
<controller name="NoCustomizeList"/>
</module>
In this way, the
Warehouse module does not allow the user to
customize the list.
Several
presentation formats using editors (new in v5.7)
The same data can be displayed with different presentation formats, for
example, using a list, charts, cards, etc. The user can choose the format
using the buttons on the right of top button bar:

The available formats are all the editors assigned to tab (using
<for-tabs/>)
in
default-editors.xml or
editors.xml. However, you
can change the editors available for a specific tab with the
editors
attribute
(new in v5.7) of
@Tab. For example, if you
write a
@Tab like this:
@Tab ( name ="WithCards", editors ="List, Charts, CustomerCardList",
properties="number, name, type, address.city, address.state.name"
)
This tab will have
List and
Charts formats, that are
standard, and a custom format,
CustomerCardList.
CustomerCardList
is a custom editor defined in
editors.xml.
To know how to define the editors for tabs, read the
customization
documentation.
Removing
the Charts from list mode (new in v5.7)
To remove
Charts (or any other list format) from a concrete list
you can use
@Tab(editors=) in this way:
@Tab ( name ="OnlyList", editors ="List",
properties="number, name, type, address.city, address.state.name"
)
Thus you can get a module without charts, just the plain original OpenXava
list. Given that there are only one editor, no button for selecting format
is shown.
The difference between
editor and
editors, is that
with
editor we indicate the editor for the default format, while
with
editors we specify all the available formats.
To remove
Charts for all the lists of your application in one
shot you can use
tabs-default-values.xml:
<?xml version = "1.0" encoding = "ISO-8859-1"?>
<!DOCTYPE tabs-default-values SYSTEM "dtds/tabs-default-values.dtd">
<tabs-default-values>
<tab editors="List">
<for-all-models/>
</tab>
</tabs-default-values>
This technique is not just for removing
Charts, it's for
restricting the list formats available for all modules. If not all the
editors
<for-tabs/> are used.