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 element
<tab/> within the component
definition.
The syntax of tab is:
<tab
name="name" <!-- 1 -->
editor="editor" <!-- 2 New in v4.6 -->
editores="editores" <!-- 3 New in v5.7 -->
>
<filter ... /> <!-- 4 -->
<row-style ... /> ... <!-- 5 -->
<properties ... /> <!-- 6 -->
<base-condition ... /> <!-- 7 -->
<default-order ... /> <!-- 8 -->
</tab>
- name (optional): You can define several tabs in a
component, and set a name for each one. This name is used to indicate
the tab that you want to use (usually in application.xml).
- 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.
- filter (one, optional): Allows to define
programmatically some logic to apply to the values entered by user
when he filters the list data.
- row-style (several, optional): A simple way to
specify a different visual style for some rows. Normally to emphasize
rows that fulfill certain condition.
- properties (one, 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.
- base-condition (one, optional): Condition to be
fulfilled by the displayed data. It's added to the user condition if
needed.
- default-order (one, optional): To specify the
initial order for data.
Initial
properties and emphasize rows
The most simple customization is to indicate the properties to show
initially:
<tab>
<row-style style="row-highlight" property="type" value="steady"/>
<properties>
name, type, seller.name, address.city, seller.level.description
</properties>
</tab>
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
<row-style/>;
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 (in versions previous to
v4m3 it was
highlight) style are already defined in OpenXava,
but you can define more.
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 class="org.openxava.test.filters.CurrentYearFilter"/>
<properties>
year, number, amountsSum, vat, detailsCount, paid, customer.name
</properties>
<base-condition>${year} = ?</base-condition>
</tab>
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
base-condition
of tab.
To sum up, the tab that you see above only shows the invoices of the
current year.
Another case:
<tab name="DefaultYear">
<filter class="org.openxava.test.filters.DefaultYearFilter"/>
<properties>
year, number, customer.number,
customer.name, amountsSum, vat, detailsCount, paid, importance
</properties>
<base-condition>${year} = ?</base-condition>
</tab>
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
controllers chapter.
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
(new
in v2.0) 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
controllers chapter.
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 using
SQL syntax:
<tab name="FromAlaska">
<base-condition>
from XAVATEST.CUSTOMER
inner join XAVATEST.CUSTOMER_STATE states1_
on XAVATEST.CUSTOMER.NUMBER=states1_.CUSTOMER
inner join XAVATEST.STATE state2_
on states1_.STATE=state2_.ID
inner join XAVATEST.STATE T_address_state
on XAVATEST.CUSTOMER.STATE=T_address_state.ID
where state2_.ID='AK'
</base-condition>
</tab>
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.
Pure
SQL select
You can write the complete select statement to obtain the tab data:
<tab name="CompleteSelect">
<properties>number, description, family</properties>
<base-condition>
select ${number}, ${description}, XAVATEST@separator@FAMILY.DESCRIPTION
from XAVATEST@separator@SUBFAMILY, XAVATEST@separator@FAMILY
where XAVATEST@separator@SUBFAMILY.FAMILY =
XAVATEST@separator@FAMILY.NUMBER
</base-condition>
</tab>
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</properties>
<default-order>${year} desc, ${number} desc</default-order>
</tab>
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)
This feature works for XML Component just in the same way
that for JPA entities.
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+</properties>
</tab>
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</properties>
</tab>
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.
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/> in your
XML component. 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</properties>
</tab>
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)
<tab editors=/> also allows you to remove the
Charts
(and any other list format) from list mode, in this way:
<tab name="OnlyList" editors="List">
<properties>number, name, type, address.city, address.state.name</properties>
</tab>
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.