Table of Contents

Chapter 4: View
Layout
Groups
Sections
Layout philosophy
Rules for view annotations
Property customization
Label format
Property value change event
Actions of property
Choosing an editor
Reference customization
Choose view
Customizing frame
Custom search action
Custom creation action
Custom modification action
Descriptions list (combos)
Reference value change event
Reference search on change event
Collection customization
Custom edit/view action
Custom list actions
Default list actions
Custom detail actions
Refining collection view default behavior
Transient properties for UI controls
View actions
Transient class: Only for creating views

Chapter 4: View

OpenXava generates a default user interface from the model. In many simple cases this is enough, but sometimes it is necessary to model with precision the format of the user interface or view. In this chapter you will learn how to do this.

Layout

The @View annotation can be used in an entity or embeddable class in order to define the layout of its members in the user interface.
The syntax for @View is:
@View(
    name="name",       // 1
    members="members"  // 2
)
public class MyEntity {
 
  1. name (optional): This name identifies the view, and can be used in other OpenXava places (for example in application.xml) or from another entity. If the view has no name then the view is assumed as the default one, that is the natural form to display an object of this type.
  2. members (optional): Indicates the members to display and its layout in the user interface. By default it displays all members (excluding hidden ones) in the order in which are declared in the model. Inside members you can use section and group elements for layout purposes; or action element for showing a link associated to a custom action inside your view.
You can define several views for an entity using the @Views annotation.
By default (if you do not use @View) all members are displayed in the order of the model, and one for each line.
For example, a model like this:
@Entity
@IdClass(ClerkKey.class)
public class Clerk {
 
    @Id @Required
    @Column(length=3, name="ZONE")
    private int zoneNumber;
 
    @Id @Required
    @Column(length=3, name="OFFICE")
    private int officeNumber;
 
    @Id  @Required
    @Column(length=3, name="NUMBER")
    private int number;
 
    @Required @Column(length=40)
    private String name;
 
    // Getters and setters
    ...
 
 }
Generates a view that looks like this:
view_en010.jpg
You can choose the members to display and its order, with the members attribute:
@Entity
@IdClass(ClerkKey.class)
@View(members="zoneNumber; officeNumber; number")
public class Clerk {
 
In this case name is not shown.
Also you can use members for tunning the layout:
@View(members=
    "zoneNumber, officeNumber, number;" +
    "name"
)
 
You can observe that the member names are separated by commas or by semicolon, this is used to indicate layout. With comma the member is placed just the following (at right), and with semicolon the next member is put below (in the next line). Hence the previous view is displayed in this way:
view_en020.jpg

Groups

With groups you can lump a set of related properties and it has visual effect. For defining a group you only need to put the name of the group and after it its members within square brackets. Just in this way:
@View(members=
    "id [ zoneNumber, officeNumber, number ];" +
    "name"
)
In this case the result is:
view_en030.jpg
You can see the three properties within the group are displayed inside a frame, and name is displayed outside this frame. The semicolon before name causes it to appear below, if not it appears at right.
You can put several groups in a view:
@View(members=
    "general [" +
    "    number;" +
    "    type;" +
    "    name;" +
    "]" +
    "contact [" +
    "    telephone;" +
    "    email;" +
    "    website;" +
    "]"
)
In this case the groups are shown one next to the other:
view_en035.jpg
If you want one below the other then you must use a semicolon after the group, like this:
@View(members=
    "general [" +
    "    number;" +
    "    type;" +
    "    name;" +
    "];" +
    "contact [" +
    "    telephone;" +
    "    email;" +
    "    website;" +
    "]"
)
In this case the view is shown this way:
view_en040.jpg
Nested groups are allowed. This is a pretty feature that allows you to layout the elements of the user interface in a flexible and simple way. For example, you can define a view as this:
@View(members=
    "invoice;" +
    "deliveryData [" +
    "    type, number;" +
    "    date;" +
    "    description;" +
    "    shipment;" +
    "    transportData [" +
    "        distance; vehicle; transportMode; driverType;" +
    "    ]" +
    "    deliveryByData [" +
    "        deliveredBy;" +
    "        carrier;" +
    "        employee;" +
    "    ]" +
    "]"
)
 
And the result will be:
view_en050.jpg
Sometimes it's useful to layout members aligned by columns, like in a table. For example, the next view:
@View(name="Amounts", members=
    "year, number;" +
    "amounts [" +
        "customerDiscount, customerTypeDiscount, yearDiscount;" +
        "amountsSum, vatPercentage, vat;" +
    "]"
)
...will be displayed as following:
view_en060.jpg
This is ugly. It would be better to have all data aligned by columns. You can define the group in this way:
@View(name="Amounts", members=
    "year, number;" +
    "amounts [#" +
        "customerDiscount, customerTypeDiscount, yearDiscount;" +
        "amountsSum, vatPercentage, vat;" +
    "]"
)
Note that now you use [# instead of [. Now you obtain this result:
view_en070.jpg
Now, thanks to the #, the members are aligned by columns.
This feature is also available for the sections (see below).

Sections

Furthermore the members can be organized in sections. For defining a section you only need to put the name of the section and after it its members within braces. Let's see an example from the Invoice entity:
@View(members=
    "year, number, date, paid;" +
    "comment;" +
    "customer { customer }" +
    "details { details }" +
    "amounts { amountsSum; vatPercentage; vat }" +
    "deliveries { deliveries }"
)
The visual result is:
view_en080.jpg
The sections are rendered as tabs that the user can click to see the data contained in that section. You can observe how in the view you put members of all types (not only properties); thus, customer is a reference, details and deliveries are collections.
Nested sections are allowed. For example, you can define a view as this:
@View(name="NestedSections", members=
    "year, number, date;" +
    "customer { customer }" +
    "data {" +
    "    details { details }" +
    "    amounts {" +
    "        vat { vatPercentage; vat }" +
    "        amountsSum { amountsSum }" +
    "    }" +
    "}" +
    "deliveries { deliveries }"
)
In this case you will obtain a user interface like this:
view_en090.jpg
As in the groups case, the sections allow using # for aligning by columns, like this:
@View(name="AlignedAmountsInSection", members=
    "year, number;" +
    "customer { customer }" +
    "details { details }" +
    "amounts {#" +
        "customerDiscount, customerTypeDiscount, yearDiscount;" +
        "amountsSum, vatPercentage, vat;" +
    "}"
)
 
With the same effect as in the group case.

Layout philosophy

It's worth to notice that you have groups instead of frames and sections instead of tabs. Because OpenXava tries to maintain a high level of abstraction, that is, a group is a set of members semantically related, and the sections allow to split the data into parts. This is useful, if there is a big amount of data that cannot be displayed simultaneous. The fact that the group is displayed as frames or sections in a tabbed pane is only an implementation issue. For example, OpenXava (maybe in future) can choose to display sections (for example) with trees or so.

Rules for view annotations

You can annotate a member (property, reference or collection) with several annotations that refine its display style and behaviour. Moreover you can define that effect of these annotations only applies to some views.
For example if you have an entity as this one:
@Entity
@Views({
    @View( members= "number; type; name; address" ),
    @View( name="A", members= "number; type; name; address; seller" ),
    @View( name="B", members= "number; type; name; seller; alternateSeller" ),
    @View( name="C", members="number; type; name; address; deliveryPlaces" )
})
public class Customer {
 
And you want the name property will be read only. You can annotate it in this way:
@ReadOnly
private String name;
In this way name is read only in all views. However, you may want that name will be read only only on views B and C, then you can define the member as following:
@ReadOnly(forViews="B, C")
private String name;
Another way for defining this same case is:
@ReadOnly(notForViews="DEFAULT, A")
private String name;
Using notForViews you indicate the views where name property is not read only. DEFAULT is used for referencing to the default view, the view with no name.
Some annotations have one or more values, for example for indicating which view of the referenced type will be used for displaying a reference you use the @ReferenceView annotation:
@ReferenceView("Simple")
private Seller seller;
In this case when the seller is displayed the view Simple, defined in Seller class, is used.
What if you want to use Simple view of Seller only in B view of Customer? It's easy:
@ReferenceView(forViews="B", value="Simple")
private Seller seller;
What if you want to use Simple view of Seller only in B view of Customer and the VerySimple view of Seller for A view of Customer? In this case you have to use several @ReferenceView grouping them with @ReferenceViews, just in this way:
@ReferenceViews({
    @ReferenceView(forViews="B", value="Simple"),
    @ReferenceView(forViews="A", value="VerySimple")
})
 
These rules apply to all the annotations in this chapter, except @View and @Views.

Property customization

You can refine the visual aspect and behavior of a property in a view using the next annotations:
@ReadOnly                   // 1
@LabelFormat                // 2
@DisplaySize                // 3
@OnChange                   // 4
@Action                     // 5
@Editor                     // 6
private type propertyName;
 
All these annotations follow the rules for view annotations and all they are optionals. OpenXava always assumes a correct default values if they are omitted.
  1. @ReadOnly (OX): If you mark a property with this annotation it never will be editable by the final user in this view. An alternative to this is to make the property editable or not editable programmatically using org.openxava.view.View.
  2. @LabelFormat (OX): Format to display the label of this property. Its value can be LabelFormatType.NORMAL, LabelFormatType.SMALL or LabelFormatType.NO_LABEL.
  3. @DisplaySize (OX): The size in characters of the editor in the User Interface used to display this property. The editor display only the characters indicated by @DisplaySize but it allows to the user to entry until the total size of the property. If @DisplaySize is not specified, the value of the size of the property is assumed.
  4. @OnChange (OX): Action to execute when the value of this property changes. Only one @OnChange action per view is allowed.
  5. @Action (OX): Actions (showed as links, buttons or images to the user) associated (visually) to this property and that the final user can execute. It's possible to define several @Action for each view.
  6. @Editor (OX): Name of the editor to use for displaying the property in this view. The editor must be declared in OpenXava/xava/default-editors.xml or xava/editors.xml of your project.

Label format

A simple example of using @LabelFormat:
@LabelFormat(LabelFormatType.SMALL)
private int zipCode;
 
In this case the zip code is displayed as:
view_en100.jpg
The LabelFormatType.NORMAL format is the default style (with a normal label at the left) and the LabelFormatType.NO_LABEL simply does not display the label.

Property value change event

If you wish to react to the event of a value change of a property you can use @OnChange as following:
@OnChange(OnChangeCustomerNameAction.class)
private String name;
 
The code to execute is:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
import org.openxava.test.model.*;
 
/**
 * @author Javier Paniza
 */
public class OnChangeCustomerNameAction extends OnChangePropertyBaseAction {  // 1
 
    public void execute() throws Exception {
        String value = (String) getNewValue();                                // 2
        if (value == null) return;
        if (value.startsWith("Javi")) {
            getView().setValue("type", Customer.Type.STEADY);                 // 3
        }
    }
 
}
The action has to implement IOnChangePropertyAction although it is more convenient to extend it from OnChangePropertyBaseAction (1). Within the action you can use getNewValue() (2) that provides the new value entered by user, and getView() (3) that allows you to access programmatically the View (change values, hide members, make them editable and so on).

Actions of property

You can also specify actions (@Action) that the user can click directly:
@Action("Delivery.generateNumber")
private int number;
 
In this case instead of an action class you have to write the action identifier that is the controller name and the action name. This action must be registered in controllers.xml in this way:
<controller name="Delivery">
    ...
    <action name="generateNumber" hidden="true"
        class="org.openxava.test.actions.GenerateDeliveryNumberAction">
        <use-object name="xava_view"/>
    </action>
    ...
</controller>
 
The actions are displayed as a link or an image beside the property. Like this:
view_en110.jpg
By default the action link is present only when the property is editable, but if the property is read only (@ReadOnly) or calculated then it is always present. You can use the attribute alwaysEnabled to true so that the link is always present, even if the property is not editable. As following:
@Action(value="Delivery.generateNumber", alwaysEnabled=true)
 
The attribute alwaysEnabled is optional and its default value is false.
The code of previous action is:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
 
/**
 * @author Javier Paniza
 */
public class GenerateDeliveryNumberAction extends ViewBaseAction {
 
    public void execute() throws Exception {
        getView().setValue("number", new Integer(77));
    }
 
}
A simple but illustrative implementation. You can use any action defined in controllers.xml and its behavior is the normal for an OpenXava action. In the chapter 7 you will learn more details about actions.
Optionally you can make your action an IPropertyAction (this is only available for actions associated to properties with @Action annotation), thus the container view and the property name are injected in the action by OpenXava. The above action class could be rewritten in this way:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
import org.openxava.view.*;
 
/**
 * @author Javier Paniza
 */
public class GenerateDeliveryNumberAction
    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;
    }
 
}
This action implements IPropertyAction (1), this required that the class implements setProperty() (3) and setView() (4), these values are injected in the action object before call to execute() method, where they can be used (2). In this case you does not need to inject xava_view object when defining the action in controllers.xml. The view injected by setView() (4) is the inner view that contains the property, for example, if the property is inside an aggregate the view is the view of that aggregate not the main view of the module. Thus, you can write more reusable actions.

Choosing an editor

An editor display the property to the user and allows him to edit its value. OpenXava uses by default the editor associated to the stereotype or type of the property, but you can specify a concrete editor for display a property using @Editor.
For example, OpenXava uses a combo for editing the properties of type enum, but if you want to display a property of this type in some particular view using a radio button you can define that view in this way:
@Editor(forViews="TypeWithRadioButton", value="ValidValuesRadioButton")
private Type type;
public enum Type { NORMAL, STEADY, SPECIAL };
 
In this case for displaying/editing the editor ValidValuesRadioButton will be used, instead of default one. ValidValueRadioButton is defined in OpenXava/xava/default-editors.xml as following:
<editor name="ValidValuesRadioButton" url="radioButtonEditor.jsp"/>
This editor is included with OpenXava, but you can create your own editors with your custom JSP code and declare them in the file xava/editors.xml of your project.
This feature is for changing the editor only in one view. If you want to change the editor for a type, steroetype or a property of a model at application level then it's better to configure it using xava/editors.xml file.

Reference customization

You can refine the visual aspect and behavior of a reference in a view using the next annotations:
@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 type referenceName;
All these annotations follow the rules for view annotations and all they are optionals. OpenXava always assumes a correct default values if they are omitted.
  1. @ReferenceView (OX): If you omit this annotation, then the default view of the referenced object is used. With this annotation you can indicate that it uses another view.
  2. @ReadOnly (OX): If you use this annotation the reference never will be editable by final user in this view. An alternative is to make the property editable/uneditable programmatically using org.openxava.view.View.
  3. @NoFrame (OX): If the reference is displayed with no frame. By default the references are displayed with frame.
  4. @NoCreate (OX): By default the final user can create new objects of the referenced type from here. If you use this annotation this will not be possible.
  5. @NoModify (OX): By default the final user can modify the current referenced object from here. If you use this annotation this will not be possible.
  6. @NoSearch (OX): By default the user will have a link to make searches with a list, filters, etc. If you use this annotation this will not be possible.
  7. @AsEmbedded (OX): By default in the case of a reference to an embeddable the user can create and edit its data, while in the case of a reference to an entity the user can only to choose an existing entity. If you put @AsEmbedded then the user interface for references to entities behaves as a in the embedded case, allowing to the user to create a new object and editing its data directly. It has no effect in case of a reference to embeddables. Warning! If you remove an entity its referenced entities are not removed, even if they are displayed using @AsEmbedded.
  8. @SearchAction (OX): Allows you to specify your own action for searching when the user click in the search link. Only one by view is allowed.
  9. @DescriptionsList (OX): Display the data as a list of descriptions, typically as a combo. Useful when there are few elements of the referenced object.
  10. @LabelFormat (OX): Format to display the label of the reference. It only applies if this reference is annotated with @DescriptionsList. Works as in property case.
  11. @Action (OX): Actions (showed as links, buttons or images to the user) associated (visually) to this reference and that the final user can execute. Works as in property case. You can define several actions for each reference in the same view.
  12. @OnChange (OX): Action to execute when the value of this reference changes. Only one @OnChange action by view is allowed.
  13. @OnChangeSearch (OX): Allows you to specify your own action for searching when the user type a new key. Only one by view is allowed.
If you do not use any of these annotations OpenXava draws a reference using the default view. For example, if you have a reference like this:
@ManyToOne
private Family family;
The user interface will look like this:
view_en120.jpg

Choose view

The most simple customization is to specify the view of the referenced object that you want to use. This is done by means of @ReferenceView:
@ManyToOne(fetch=FetchType.LAZY)
@ReferenceView("Simple")
private Invoice invoice;
 
In the Invoice entity you must have a view named Simple:
@Entity
@Views({
    ...
    @View(name="Simple", members="year, number, date, yearDiscount;"),
    ...
})
public class Invoice {
Thus, instead of using the default view of Invoice (that shows all invoice data) OpenXava will use the next one:
view_en130.jpg

Customizing frame

If you combine @NoFrame with groups you can group visually a property that is not a part of a reference with that reference, for example:
@View( members=
    ...
    "seller [" +
    "    seller; " +
    "    relationWithSeller;" +
    "]" +
    ...
)
public class Customer {
    ...
    @ManyToOne(fetch=FetchType.LAZY)
    @NoFrame
    private Seller seller;
    ...
}
 
And the result:
view_en140.jpg

Custom search action

The final user can search a new value for the reference simply by keying the new code and leaving the editor the data of reference is obtained; for example, if the user keys "1" on the seller number field, then the name (and the other data) of the seller "1" will be automatically filled. Also the user can click in the lantern, in this case the user will go to a list where he can filter, order, etc, and mark the wished object.
To define your custom search logic you have to use @SearchAction in this way:
@ManyToOne(fetch=FetchType.LAZY) @SearchAction("MyReference.search")
private Seller seller;
 
When the user clicks in the lantern your action is executed, which must be defined in controllers.xml.
<controller name="MyReference">
    <action name="search" hidden="true"
        class="org.openxava.test.actions.MySearchAction"
        image="images/search.gif">
        <use-object name="xava_view"/>
        <use-object name="xava_referenceSubview"/>
        <use-object name="xava_tab"/>
        <use-object name="xava_currentReferenceLabel"/>
    </action>
    ...
</controller>
The logic of your MySearchAction is up to you. You can, for example, refining the standard search action to filter the list for searching, as follows:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
 
/**
 * @author Javier Paniza
 */
 
public class MySearchAction extends ReferenceSearchAction {
 
    public void execute() throws Exception {
        super.execute();    // The standard search behaviour
        getTab().setBaseCondition("${number} < 3"); // Adding a filter to the list
    }
 
}
You will learn more about actions in chapter 7.

Custom creation action

If you do not use @NoCreate annotation the user will have a link to create a new object. By default when a user clicks on this link, a default view of the referenced object is displayed and the final user can type values and click a button to create it. If you want to define your custom actions (among them your create custom action) in the form used when creating a new object, you must have a controller named as component but with the suffix Creation. If OpenXava see this controller it uses it instead of the default one to allow creating a new object from a reference. For example, you can write in your controllers.xml:
<!--
Because its name is WarehouseCreation (model name + Creation) it is used
by default for create from reference, instead of NewCreation.
Action 'new' is executed automatically.
-->
<controller name="WarehouseCreation">
    <extends controller="NewCreation"/>
    <action name="new" hidden="true"
        class="org.openxava.test.actions.CreateNewWarehouseFromReferenceAction">
        <use-object name="xava_view"/>
    </action>
</controller>
In this case when the user clicks on the 'create' link, the user is directed to the default view of Warehouse and the actions in WarehouseCreation will be allowed.
If you have an action called 'new', it will be executed automatically before all. It can be used to initialize the view used to create a new object.

Custom modification action

If you do not use @NoModify the user will have a link to modify the current referenced object. By default when a user clicks on this link, a default view of the referenced object is displayed and the final user can modify values and click a button to update it. If you want to define your custom actions (among them your update custom action) in the form used when modifying the current object, you must have a controller named as component but with the suffix Modification. If OpenXava see this controller it uses it instead of the default one to allow modifying the current object from a reference. For example, you can write in your controllers.xml:
<!--
Because its name is WarehouseModification (model name + Modification) it is used
by default for modifying from reference, instead of Modification.
The action 'search' is executed automatically.
-->
<controller name="WarehouseModification">
    <extends controller="Modification"/>
    <action name="search" hidden="true"
        class="org.openxava.test.actions.ModifyWarehouseFromReferenceAction">
        <use-object name="xava_view"/>
    </action>
</controller>
In this case when the user clicks on the 'modify' link, the user is directed to the default view of Warehouse and the actions in WarehouseModification will be allowed.
If you have an action called 'search', it will be executed automatically before all. It is used to initialize the view with the object to modify.

Descriptions list (combos)

With @DescriptionsList you can instruct OpenXava to visualize references as a descriptions list (actually a combo). This can be useful, if there are only a few elements and these elements have a significant name or description.
The syntax is:
@DescriptionsList(
    descriptionProperties="properties",  // 1
    depends="depends",                   // 2
    condition="condition",               // 3
    orderByKey="true|false",             // 4
    order="order"                        // 5
)
  1. descriptionProperties (optional): The property or properties to show in the list, if not specified, the property named description, descripcion, name or nombre is assumed. If the referenced object does not have a property called this way then it is required to specify a property name here. It allows to set more than one property separated by commas. To the final user the values are concatenated.
  2. depends (optional): It's used in together with condition. It can be achieve that the list content depends on another value displayed in the main view (if you simply type the name of the member) or in the same view (if you type this. before the name of the member).
  3. condition (optional): Allows to specify a condition (with SQL style) to filter the values that are shown in the description list.
  4. orderByKey (optional): By default the data is ordered by description, but if you set this property to true it will be ordered by key.
  5. order (optional): Allows to specify an order (with SQL style) for the values that are shown in the description list.
The simplest usage is:
@ManyToOne(fetch=FetchType.LAZY)
@DescriptionsList
private Warehouse warehouse;
 
That displays a reference to warehouse in this way:
view_en150.jpg
In this case it shows all warehouses, although in reality it uses the baseCondition and the filter specified in the default @Tab of Warehouse. You will see more about tabs in chapter 5.
If you want, for example, to display a combo with the product families and when the user chooses a family, then another combo will be filled with the subfamilies of the chosen family. An implementation can look like this:
@ManyToOne(fetch=FetchType.LAZY)
@DescriptionsList(orderByKey=true)            // 1
private Family family;
 
@ManyToOne(fetch=FetchType.LAZY) @NoCreate    // 2
@DescriptionsList(
    descriptionProperties="description",      // 3
    depends="family",                         // 4
    condition="${family.number} = ?"          // 5
    order="${description} desc"               // 6
 )
private Subfamily subfamily;
 
Two combos are displayed one with all families loaded and the other one empty. When the user chooses a family, then the second combo is filled with all its subfamilies.
In the case of Family the property description of Family is shown, since the default property to show is 'description' or 'name'. The data is ordered by key and not by description (1). In the case of Subfamily (2) the link to create a new subfamily is not shown and the property to display is 'description' (in this case this maybe omitted).
With depends (4) you make that this combo depends on the reference family, when change family in the user interface, this descriptions list is filled applying the condition condition (5) and sending as argument (to set value to ?) the new family value. And the entries are ordered descending by description (6).
In condition and order you put the property name inside a ${} and the arguments as ?. The comparator operators are the SQL operators.
You can specify several properties to be shown as description:
@ManyToOne(fetch=FetchType.LAZY)
@ReadOnly
@DescriptionsList(descriptionProperties="level.description, name")
private Seller alternateSeller;
 
In this case the concatenation of the description of level and the name is shown in the combo. Also you can see how it is possible to use qualified properties (level.description).
If you use @ReadOnly in a reference annotated as @DescriptionsList, then the description (in this case level.description + name) is displayed as a simple text property instead of using a combo.

Reference value change event

If you wish to react to the event of a value change of a reference you can write:
@ManyToOne(fetch=FetchType.LAZY)
@OnChange(OnChangeCarrierInDeliveryAction.class)
private Carrier carrier;
 
In this case your action listens to the change of carrier number.
The code to execute is:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
 
/**
 * @author Javier Paniza
 */
public class OnChangeCarrierInDeliveryAction
    extends OnChangePropertyBaseAction {                                        // 1
 
    public void execute() throws Exception {
        if (getNewValue() == null) return;
        getView().setValue("remarks", "The carrier is " + getNewValue());
        addMessage("carrier_changed");
    }
 
}
The action implements IOnChangePropertyAction, by means of OnChangePropertyBaseAction (1), although it's a reference. We receive the change of the key property of the reference; in this case carrier.number. The rest is as in the property case.

Reference search on change event

The user can search the value of a reference simply typing its key. For example, if there is a reference to Subfamily, the user can type the subfamily number and automatically the subfamily data is loaded in the view. This is done using a default on change action that does the search. You can specify your own action for search when key change using @OnChangeSearch annotation, just in this way:
@ManyToOne(fetch=FetchType.LAZY)
@OnChangeSearch(OnChangeSubfamilySearchAction.class)
private Subfamily subfamily;
This action is executed for doing the search, instead of the standard action, when the user changes the subfamily number.
The code to execute is:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
 
/**
 *
 * @author Javier Paniza
 */
 
public class OnChangeSubfamilySearchAction
    extends OnChangeSearchAction {  // 1
 
    public void execute() throws Exception {
        if (getView().getValueInt("number") == 0) {
            getView().setValue("number", new Integer("1"));
        }
        super.execute();
    }
 
}
 
The action implements IOnChangePropertyAction, by means of OnChangeSearchAction (1), although it's a reference. It receives the change of the key property of the reference; in this case subfamily.number.
This case is an example of refining the behaviour of on change search, because it extends from OnChangeSearchAction, that is the default action for searching, and calls to super.execute(). Also it's possible to do a regular on change action (extending from OnChangePropertyBaseAction for example) overriding completely the search logic.

Collection customization

You can refine the visual aspect and behavior of a collection in a view using the next annotations:
@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 collectionName;
 
All these annotations follow the rules for view annotations and all they are optionals. OpenXava always assumes a correct default values if they are omitted.
  1. @CollectionView (OX): The view of the referenced object (each collection element) which is used to display the detail. By default the default view is used.
  2. @ReadOnly (OX): If you set it then the final user only can view collection elements, he cannot add, delete or modify elements.
  3. @EditOnly (OX): If you set it then the final user can modify existing elements, but not add or remove collection elements.
  4. @NoCreate (OX): If you set it then the final user doesn't get the link to create new objects of the referenced object type. It does not apply to embedded collections.
  5. @NoModify (OX): If you set it then the final user doesn't get the link to modify the objects of the referenced object type. It does not apply to embedded collections.
  6. @AsEmbedded (OX): By default the embedded collections (with cascade type REMOVE or ALL) allow the users to create and to edit elements, while the regular collections allow only to choose existing entities to add to (or remove from) the collection. If you put @AsEmbedded then the collection behaves as a embedded collection even though it hasn't cascade type REMOVE or ALL, allowing to the user to add objects and editing them directly. It has no effect in case of embedded collections.
  7. @ListProperties (OX): Properties to show in the list for visualization of the collection. You can qualify the properties. By default it shows all persistent properties of the referenced object (excluding references and calculated properties).
  8. @RowStyle (OX): To give a special style to some rows. Behaves equals that in the Tab case. It does not works for calculated collections. It's possible to define several @RowStyle for each view.
  9. @EditAction (OX): Allows you to define your custom action to begin the editing of a collection element. This is the action showed in each row of the collection, if the collection is editable. Only one @EditAction per view is allowed.
  10. @ViewAction (OX): Allows you to define your custom action to view a collection element. This is the action showed in each row, if the collection is read only. Only one @ViewAction per view is allowed.
  11. @NewAction (OX): Allows you to define your custom action to start adding a new element to the collection. This is the action executed on click in 'Add' link. Only one @ViewAction per view is allowed.
  12. @SaveAction (OX): Allows you to define your custom action to save the collection element. This is the action executed on click in 'Save detail' link. Only one @SaveAction per view is allowed.
  13. @HideDetailAction (OX): Allows you to define your custom action to hide the detail view. This is the action executed on click in 'Close' link. Only one @HideDetailAction per view is allowed.
  14. @RemoveAction (OX): Allows you to define your custom action to remove the element from the collection. This is the action executed on click in 'Remove detail' link. Only one @RemoveAction per view is allowed.
  15. @RemoveSelectedAction (OX): Allows you to define your custom action to remove the selected elements from the collection. This is the action executed when a user select some rows and then click in 'Remove selected' link. Only one @RemoveSelectedAction per view is allowed.
  16. @ListAction (OX): To add actions in list mode; usually actions which scope is the entire collection. It's possible to define several @ListAction for each view.
  17. @DetailAction (OX): To add actions in detail mode, usually actions which scope is the detail that is being edited. It's possible to define several @DetailAction for each view.
If you do not use any of these annotations then the collection is displayed using the persistent properties in list mode and the default view to represent the detail; although in typical scenarios the properties of the list and the view for detail are specified:
@CollectionView("Simple"),
@ListProperties("number, name, remarks, relationWithSeller, seller.level.description, type")
@OneToMany(mappedBy="seller")
private Collection<Customer> customers;
 
And the collection is displayed:
view_en160.jpgYou see how you can put qualified properties into the properties list (as seller.level.description).
When the user clicks on edit.gif ('Edit'), then the view Simple of Customer will be rendered; for this you must have defined a view called Simple in the Customer entity (the model of the collection elements).
This view is also used if the user click on create_new.gif ('Add') in an embedded collection, otherwise OpenXava does not show this view, instead it shown a list of entities to add.
If the view Simple of Customer is like this:
@View(name="Simple", members="number; type; name; address")
On clicking in a detail the following will be shown:
view_en170.jpg

Custom edit/view action

You can refine easily the behavior when the edit.gif ('Edit') link is clicked using @EditAction:
@EditAction("Invoice.editDetail")
@OneToMany (mappedBy="invoice", cascade=CascadeType.REMOVE)
private Collection<InvoiceDetail> details;
 
You have to define Invoices.editDetail in controllers.xml:
<controller name="Invoice">
    ...
    <action name="editDetail" hidden="true"
        image="images/edit.gif"
        class="org.openxava.test.actions.EditInvoiceDetailAction">
        <use-object name="xava_view"/>
    </action>
    ...
</controller>
And finally write your action:
package org.openxava.test.actions;
 
import java.text.*;
 
import org.openxava.actions.*;
 
/**
 * @author Javier Paniza
 */
public class EditInvoiceDetailAction extends EditElementInCollectionAction {       // 1
 
    public void execute() throws Exception {
        super.execute();
        DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
        getCollectionElementView().setValue(                               // 2
            "remarks", "Edit at " + df.format(new java.util.Date()));
    }
 
}
In this case you only refine hence your action extends (1) EditElementInCollectionAction. In this case you only specify a default value for the remarks property. Note that to access the view that displays the detail you can use the method getCollectionElementView() (2).
Also it's possible to remove the edit action from the User Interface, in this way:
@EditAction("")
@OneToMany (mappedBy="invoice", cascade=CascadeType.REMOVE)
private Collection<InvoiceDetail> details;
You only need to put an empty string as value for the action. Although in most case it's enough to define the collection as @ReadOnly.
The technique to refine the view action (the action for each row, if the collection is read only) is the same but using @ViewAction instead of @EditAction.

Custom list actions

Adding our custom list actions (actions that apply to entire collections) is easy using @ListAction:
@ListAction("Carrier.translateName")
private Collection<Carrier> fellowCarriers;
Now a new link is shown to the user:
view_en180.jpg
Also you need to define the action in controllers.xml:
<controller name="Carrier">
    ...
    <action name="translateName" hidden="true"
        class="org.openxava.test.actions.TranslateCarrierNameAction">
    </action>
    ...
</controller>
 
And the action code:
package org.openxava.test.actions;
 
import java.util.*;
import org.openxava.actions.*;
import org.openxava.test.model.*;
 
/**
 * @author Javier Paniza
 */
public class TranslateCarrierNameAction extends CollectionBaseAction {  // 1
 
    public void execute() throws Exception {
        Iterator it = getSelectedObjects().iterator();                  // 2
        while (it.hasNext()) {
            Carrier carrier = (Carrier) it.next();
            carrier.translate();
        }
    }
 
}
 
The action extends CollectionBaseAction (1), this way you can use methods as getSelectedObjects() (2) that returns a collection with the objects selected by the user. There are others useful methods, as getObjects() (all elements collection), getMapValues() (the collection values in map format) and getMapsSelectedValues() (the selected elements in map format).
As in the case of detail actions (see next section) you can use getCollectionElementView().
Also it's possible to use actions for list mode as list actions for a collection.

Default list actions

If you want to add some custom list actions to all the collection of your application you can do it creating a controller called DefaultListActionsForCollections in your own xava/controllers.xml file as following:
<controller name="DefaultListActionsForCollections">
    <extends controller="Print"/>
    <action name="exportAsXML"
        class="org.openxava.test.actions.ExportAsXMLAction">
    </action>
</controller>
In this way all the collections will have the actions of Print controller (for export to Excel and generate PDF report) and your own ExportAsXMLAction. This has the same effect of @ListAction (look at custom list actions section) but it applies to all collections at once.
This feature does not apply to calculated collections.

Custom detail actions

Also you can add your custom actions to the detail view used for editing each element. This is accomplish by means of @DetailAction annotation. These actions are applicable only to one element of collection. For example:
@DetailAction("Invoice.viewProduct")
@OneToMany (mappedBy="invoice", cascade=CascadeType.REMOVE)
private Collection<InvoiceDetail> details;
In this way the user has another link to click in the detail of the collection element:
view_en190.jpg
You need to define the action in controllers.xml:
<controller name="Invoice">
    ...
    <action name="viewProduct" hidden="true"
        class="org.openxava.test.actions.ViewProductFromInvoiceDetailAction">
        <use-object name="xava_view"/>
        <use-object name="xavatest_invoiceValues"/>
    </action>
    ...
</controller>
And the code of your action:
package org.openxava.test.actions;
 
import java.util.*;
import javax.ejb.*;
 
import org.openxava.actions.*;
 
/**
 * @author Javier Paniza
 */
public class ViewProductFromInvoiceDetailAction
    extends CollectionElementViewBaseAction                 // 1
    implements INavigationAction {
 
    private Map invoiceValues;
 
    public void execute() throws Exception {
        try {
            setInvoiceValues(getView().getValues());
            Object number =
                getCollectionElementView().getValue("product.number"); // 2
            Map key = new HashMap();
            key.put("number", number);
            getView().setModelName("Product");      // 3
            getView().setValues(key);
            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 [] { "ProductFromInvoice" };
    }
 
    public String getCustomView() {
        return SAME_VIEW;
    }
 
    public Map getInvoiceValues() {
        return invoiceValues;
    }
 
    public void setInvoiceValues(Map map) {
        invoiceValues = map;
    }
 
}
You can see that it extends CollectionElementViewBaseAction (1) thus it has available the view that displays the current element using getCollectionElementView() (2). Also you can get access to the main view using getView() (3). In chapter 7 you will see more details about writing actions.
Also, using the view returned by getCollectionElementView() you can add and remove programmatically detail and list actions with addDetailAction(), removeDetailAction(), addListAction() and removeListAction(), see API doc for org.openxava.view.View.

Refining collection view default behavior

Using @NewAction, @SaveAction, @HideDetailAction, @RemoveAction and @RemoveSelectedAction you can refine the default behavior of collection view. For example if you want to refine the behavior of save a detail action you can define your view in this way:
@SaveAction("DeliveryDetail.save")
@OneToMany (mappedBy="delivery", cascade=CascadeType.REMOVE)
private Collection<DeliveryDetail> details;
 
You must have an action DeliveryDetails.save in your controllers.xml:
<controller name="DeliveryDetail">
    ...
    <action name="save"
        class="org.openxava.test.actions.SaveDeliveryDetailAction">
        <use-object name="xava_view"/>
    </action>
    ...
</controller>
And define your action class for saving:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
 
/**
 * @author Javier Paniza
 */
 
public class SaveDeliveryDetailAction extends SaveElementInCollectionAction { // 1
 
    public void execute() throws Exception {
        super.execute();
        // Here your own code                                                 // 2
    }
 
}
The more common case is extending the default behavior, for that you have to extend the original class for saving a collection detail (1), that is SaveElementInCollection action, then call to super from execute() method (2), and after it, writing your own code.
Also it's possible to remove any of these actions from User Interface, for example, you can define a collection in this way:
@RemoveSelectedAction("")
@OneToMany (mappedBy="delivery", cascade=CascadeType.REMOVE)
private Collection<DeliveryDetail> details;
In this case the action for removing the selected elements in the collection will be missing in the User Interface. As you see, only it's needed to declare an empty string as the name of the action.

Transient properties for UI controls

With @Transient (JPA) you define a property that is not stored in database but you want to show to the user. You can use it to provide UI controls to allow the user to manage his user interface.
An example:
@Transient
@DefaultValueCalculator(value=EnumCalculator.class,
    properties={
        @PropertyValue(name="enumType", value="org.openxava.test.model.Delivery$DeliveredBy")
        @PropertyValue(name="value", value="CARRIER")
    }
)
@OnChange(OnChangeDeliveryByAction.class)
private DeliveredBy deliveredBy;
public enum DeliveredBy { EMPLOYEE, CARRIER }
 
You can see that the syntax is exactly the same as in the case of a regular property of an entity; you can even use enum and @DefaultValueCalculator. After defining the property you can use it in the view as usual, for example with @OnChange or putting it as member of a view.

View actions

In addition of associating actions to a property, reference or collection, you also can define arbitrary actions inside your view, in any place. In order to do this we use the qualified name of action using brackets () as suffix, in this way:
@View( members=
    "number;" +
    "type;" +
    "name, Customer.changeNameLabel();" +
    ...
 
The visual effect will be:
view_en200.jpg
You can see the link 'Change name label' that will execute the action Customer.changeNameLabel on click on it.
If the container view of the action is not editable, the action is not present. If you want that the action is always enabled, even if the view is not editable, you have to use put the word ALWAYS between the brackets, as following:
@View( name="Simple", members=
    "number;" +
    "type;" +
    "name, Customer.changeNameLabel(ALWAYS);" +
    ...
 
The standard way to expose actions to the user is using the controllers (actions in a bar), the controllers are reusable between views, but sometimes you will need an action specific to a view, and you want display it inside the view (not in the button bar), for these cases the view actions may be useful.
See more about actions in chapter 7.

Transient class: Only for creating views

In OpenXava it is not possible to have a view without model. Thus if you want to draw an arbitrary user interface, you need to create a class, not to declare it as entity and define your view in it.
An transient class is not associated to any table of the database, typically it's used only for display User Interfaces not related to any data in database.
An example can be:
package org.openxava.test.model;
 
import javax.persistence.*;
 
import org.openxava.annotations.*;
 
/**
 * Example of an transient OpenXava model class (not persistent). <p>
 *
 * This can be used, for example, to display a dialog,
 * or any other graphical interface.<p>
 *
 * Note that is not marked as @Entity <br>
 *
 * @author Javier Paniza
 */
 
@Views({
    @View(name="Family1", members="subfamily"),
    @View(name="Family2", members="subfamily"),
    @View(name="WithSubfamilyForm", members="subfamily"),
    @View(name="Range", members="subfamily; subfamilyTo")
})
public class FilterBySubfamily {
 
    @ManyToOne(fetch=FetchType.LAZY) @Required
    @NoCreate(forViews="Family1, Family2")
    @NoModify(forViews="Family2, WithSubfamilyForm")
    @NoSearch(forViews="WithSubfamilyForm")
    @DescriptionsLists({
        @DescriptionsList(forViews="Family1",
            condition="${family.number} = 1", order="${number} desc"
        ),
        @DescriptionsList(forViews="Family2",
            condition="${family.number} = 2"
        )
    })
    private Subfamily subfamily;
 
    @ManyToOne(fetch=FetchType.LAZY)
    private Subfamily subfamilyTo;
 
    public Subfamily getSubfamily() {
        return subfamily;
    }
 
    public void setSubfamily(Subfamily subfamily) {
        this.subfamily = subfamily;
    }
 
    public Subfamily getSubfamilyTo() {
        return subfamilyTo;
    }
 
    public void setSubfamilyTo(Subfamily subfamilyTo) {
        this.subfamilyTo = subfamilyTo;
    }
 
}
 
For defining a model class as transient you only need to define a regular Java class without @Entity annotation. You mustn't put the mapping annotations nor declare properties as key.
This way you can design a dialog that can be useful, for example, to print a report of families or products filtered by subfamily.
With this simple trick you can use OpenXava as a simple and flexible generator for user interfaces although the displayed data won't be stored.