Reference guide: Model | View | Tabular data | Object/relational mapping | Controllers | Application | Customizing

Tabular data

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:
tab_en010.jpg
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 {
 
  1. name (optional): You can define several tabs in a entity (using @Tabs annotation), and set a name for each one. This name is used to indicate the tab that you want to use (usually in application.xml).
  2. filter (optional): Allows to define programmatically some logic to apply to the values entered by user when he filters the list data.
  3. 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.
  4. 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.
  5. baseCondition (optional): Condition to be fulfilled by the displayed data. It's added to the user condition if needed.
  6. defaultOrder (optional): To specify the initial order for data.
  7. 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.
  8. 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:
tab_en020.jpg

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:
tab_en030.jpg
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:
tab_en040.jpg
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:
tab050.png
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.