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.
The syntax for view is:
<view
name="name" <!-- 1 -->
label="label" <!-- 2 -->
model="model" <!-- 3 -->
members="members" <!-- 4 -->
extends="view" <!-- 5 New in v3.1.2 -->
>
<property ... /> <!-- 6 -->
<property-view ... /> <!-- 7 -->
<reference-view ... /> <!-- 8 -->
<collection-view ... /> <!-- 9 -->
<members ... /> <!-- 10 -->
</view>
- name (optional): This name identifies the view, and
can be used in other OpenXava places (for example in application.xml)
or from another component. 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. If the name is Simple or Search
this view will be used by default to create the search dialog (since
v4m4).
- label (optional): The label that is showed to the
user, if needed, when the view is displayed. It's much better use the
i18n files.
- model (optional): If the view is for an aggregate
of this component you need to specify here the name of that aggregate.
If model is not specified then this view is for the main entity.
- members (optional): List of members to show. By
default it displays all members (excluding hidden ones) in the order
in which are declared in the model. This attribute is mutually
exclusive with the members element (that you will see below).
- extends (optional): (New in v3.1.2) All
the members in the extends view are automatically included
in the current one. See view
inheritance.
- property (several, optional): Defines a property of
the view, that is, information that can be displayed to the user and
the programmer can work programmatically with it, but it is not a part
of the model.
- property-view (several, optional): Defines the
format to display a property.
- reference-view (several, optional): Defines the
format to display a reference.
- collection-view (several, optional): Defines the
format to display a collection.
- members (one, optional): Indicates the members to
display and its layout in the user interface. Is mutually exclusive
with the members attribute. Inside members you can use section and group elements
for layout purposes; or action (new in v2.0.3) element for
showing a link associated to a custom action inside your view (see below).
Layout
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>
<property name="zoneNumber" key="true"
size="3" required="true" type="int"/>
<property name="officeNumber" key="true"
size="3" required="true" type="int"/>
<property name="number" key="true"
size="3" required="true" type="int"/>
<property name="name" type="String"
size="40" required="true"/>
</entity>
Generates a view that looks like this:
You can choose the members to display and its order, with the members
attribute:
<view members="zoneNumber; officeNumber; number"/>
In this case name is not shown.
The members also can be specified using the members element, that is
mutually exclusive with the members attribute, thus:
<view>
<members>
zoneNumber, officeNumber, number;
name
</members>
</view>
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:
Groups
With groups you can lump a set of related properties and it has this
visual effect:
<view>
<members>
<group name="id">
zoneNumber, officeNumber, number
</group>
; name
</members>
</view>
In this case the result is:
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:
<group name="customer">
type;
name;
</group>
<group name="seller">
seller;
relationWithSeller;
</group>
In this case the groups are shown one next to the other:
If you want one below the other then you must use a semicolon after the
group, like this:
<group name="customer">
type;
name;
</group>;
<group name="seller">
seller;
relationWithSeller;
</group>
In this case the view is shown this way:
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:
<members>
invoice;
<group name="deliveryData">
type, number;
date;
description;
shipment;
<group name="transportData">
distance; vehicle; transportMode; driverType;
</group>
<group name="deliveryByData">
deliveredBy;
carrier;
employee;
</group>
</group>
</members>
And the result will be:
New in v2.0.4: 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;
<group name="amounts">
customerDiscount, customerTypeDiscount, yearDiscount;
amountsSum, vatPercentage, vat;
</group>
</members>
</view>
...will be displayed as following:
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;
<group name="amounts" aligned-by-columns="true"> <!-- 1 -->
customerDiscount, customerTypeDiscount, yearDiscount;
amountsSum, vatPercentage, vat;
</group>
</members>
</view>
And you will obtain this result:
Now, thanks to the
aligned-by-columns (1) attribute, the members
are aligned by columns.
The attribute
aligned-by-columns is also available for the
sections (see below) and for
members, that is for the root view
(new in v4.7.1). Moreover, if you put
alignedByColumns=true
(new in v4.7.1) in
xava.properties all elements are
aligned by column even if you do not specify
aligned-by-columns
in the view.
Sections
Furthermore the members can be organized in sections. Let's see an example
from the
Invoice component:
<view>
<members>
year, number, date, paid;
customerDiscount, customerTypeDiscount, yearDiscount;
comment;
<section name="customer">customer</section>
<section name="details">details</section>
<section name="amounts">amountsSum; vatPercentage; vat</section>
<section name="deliveries">deliveries</section>
</members>
</view>
The visual result is:
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
is a collection of aggregates and deliveries is a collection of entities.
Nested sections are allowed
(new in v2.0). For example, you can
define a view as this:
<view name="NestedSections">
<members>
year, number
<section name="customer">customer</section>
<section name="data">
<section name="details">details</section>
<section name="amounts">
<section name="vat">vatPercentage; vat</section>
<section name="amountsSum">amountsSum</section>
</section>
</section>
<section name="deliveries">deliveries</section>
</members>
</view>
In this case you will obtain a user interface like this:
New in v2.0.4: As in the groups case, the sections allow using
the attribute
aligned-by-columns, like this:
<section name="amounts" aligned-by-columns="true"> ... </section>
With the same effect as in the
group
case.
View
inheritance (new in v3.1.2)
On defining a new view you can inherit the members and layout of an
already existing view. In this way, you can avoid copy & pase, and at
the same time you keep your code shorter and easier to update.
This is done using
extends. For example if you have a view like
the next one:
<view name="VerySimple">
<members>name, sex</members>
</view>
That produces the next UI:
If you want a new view that extends this one just write the next code:
<view name="Simple" extends="VerySimple">
<members>mainLanguage</members>
</view>
and you will have the next view:
As you see the members of
VerySimple view are included
automatically in
Simple view, and the own members of view are
added at the end.
If you want to extend the default view (the default view is the view with
no name) just use the word DEFAULT as name for
extends. As in
the next example:
<view>
<members>
name, sex;
mainLanguage, favouriteFramework;
experiences
</members>
</view>
<view name="Complete" extends="DEFAULT">
<members>frameworks</members>
</view>
The
Complete view will have all the members of default view (
name,
sex, mainLanguage, favouriteFramework, experiences) plus
frameworks.
View inheritance only applies to members and their layout. Actions, events
and other refinements done at member level are not inherited.
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.
Property
view
With
<property-view/> you can refine the visual aspect and
behavior of a property in a view.
It has this syntax:
<property-view
property="propertyName" <!-- 1 -->
label="label" <!-- 2 -->
read-only="true|false" <!-- 3 -->
label-format="NORMAL|SMALL|NO_LABEL" <!-- 4 -->
editor="editorName" <!-- 5 new in v2.1.3 -->
display-size="size" <!-- 6 new in v2.2.1 -->
label-style="style" <!-- 7 new in v5.8 -->
>
<on-change ... /> <!-- 8 -->
<action ... /> ... <!-- 9 -->
</property-view>
- property (required): Usually the name of a model
property, although it also can be the name of a property of the view
itself.
- label (optional): Modifies the label for this
property in this view. To achieve this it is much better use the i18n
files.
- read-only (optional): If you set this property to
true 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.
- label-format (optional): Format to display the
label of this property.
- editor (optional): (New in v2.1.3) 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.
- display-size (optional): (New in v2.2.1)
The size in characters of the editor in the User Interface used to
display this property. The editor display only the characters
indicated by display-size but it allows to the user to entry
until the total size of the property. If display-size is not
specified, the value of the size of the property is assumed.
- label-style (optional): (New in v5.8)
Style to display the label of this property. It has the predefined
styles 'bold-label', 'italic-label' and 'reverse-label'; though you can use your own custom style if you
define it in web/xava/style/custom.css.
- on-change (one, optional): Action to execute when
the value of this property changes.
- action (several, optional): Actions (showed as
links, buttons or images to the user) associated (visually) to this
property and that the final user can execute.
Label
format
A simple example of using label format:
<view model="Address">
<property-view property="zipCode" label-format="SMALL"/>
</view>
In this case the zip code is displayed as:
The NORMAL format is the default style (with a normal label at the left)
and the NO_LABEL simply does not display the label.
Value
change event
If you wish to react to the event of a value change of a property you can
write:
<property-view property="carrier.number">
<on-change class="org.openxava.test.actions.OnChangeCarrierInDeliveryAction"/>
</property-view>
You can see how the property can be qualified, that is in this case your
action listens to the change of carrier number (carrier is a reference).
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; // 2
getView().setValue("remarks", "The carrier is " + getNewValue()); // 3
addMessage("carrier_changed");
}
}
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 that the user can click directly:
<property-view property="number">
<action action="Deliveries.generateNumber"/>
</property-view>
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="Deliveries">
...
<action name="generateNumber" hidden="true"
class="org.openxava.test.actions.GenerateDeliveryNumberAction">
<use-object name="xava_view"/> <!-- Not needed since v4m2 -->
</action>
...
</controller>
The actions are displayed as a link or an image beside the property. Like
this:
By default the action link is present only when the property is editable,
but if the property is
read-only or calculated then it is always
present. You can use the attribute
always-enabled (new in
v2.0.3) to true so that the link is always present, even if the
property is not editable. As following:
<action action="Deliveries.generateNumber" always-enabled="true"/>
The attribute
always-enabled 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
controllers
chapter you can learn more details about actions.
Optionally you can make your action an
IPropertyAction (new in v2.0.2)
(this is available for actions used in
<property-view/>
only), 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 (property, new in v2.1.3)
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 to display a
property in a view.
For example, OpenXava uses a combo for editing the properties of type
valid-values,
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:
<view name="TypeWithRadioButton">
<property-view property="type" editor="ValidValuesRadioButton"/>
<members>number; type; name; address</members>
</view>
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.
Label
style (new in v5.8)
This syntax:
<view>
<property-view property="number" label-style="italic-label red-label"/>
</view>
Produces:
We can defined the style in web/xava/styles/custom.css, in this case we
have add:
.red-label{
color:red;
font-weight:bold;
}
Reference
view
With
<reference-view/> you can modify the format for
displaying references.
Its syntax is:
<reference-view
reference="reference" <!-- 1 -->
view="view" <!-- 2 -->
read-only="true|false" <!-- 3 -->
frame="true|false" <!-- 4 -->
create="true|false" <!-- 5 -->
modify="true|false" <!-- 6 new in v2.0.4 -->
search="true|false" <!-- 7 -->
as-aggregate="true|false" <!-- 8 new in v2.0.3 -->
editor="editorName" <!-- 9 new in v3.1.3 -->
collapsed="true|false" <!-- 10 new in v5.0 -->
>
<search-list-condition ... /> <!-- 11 new in v4m4 -->
<on-change-search ... /> <!-- 12 new in v2.2.5 -->
<search-action ... /> <!-- 13 -->
<descriptions-list ... /> <!-- 14 -->
<action ... /> ... <!-- 15 new in v2.0.1 -->
</reference-view>
- reference (required): Name of the reference to
refine its presentation.
- view (optional): If you omit this attribute, then
the default view of the referenced object is used. With this attribute
you can indicate that it uses another view.
- read-only (optional): If you set the value to true
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.
- frame (optional): If the reference is displayed
inside a frame. The default value is true.
- create (optional): If the final user can create new
objects of the referenced type from here. The default value is true.
- modify (optional): (New in v2.0.4) If the
final user can modify the current referenced object from here. The
default value is true.
- search (optional): If the user will have a link to
make searches with a list, filters, etc. The default value is true.
- as-aggregate (optional): (New in v2.0.3)
By default false. By default in the case of a reference to an
aggregate 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 as-aggregate to true then the user
interface for references to entities behaves as a in the aggregate
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 aggregates.
Warning! If you remove an entity its referenced entities are not
removed, even if they are displayed using as-aggregate="true".
- editor (optional): (New in v3.1.3) Name
of the editor to use for displaying the reference in this view. The
editor must be declared in OpenXava/xava/default-editors.xml
or xava/editors.xml of your project.
- collapsed (optional): (New in v5.0) The
reference will be shown collapsed. Visually this means that the frame
surrounding the reference view will be initially closed. Later the
user will be able to set his preferences by clicking on the expansion
icon. The default value is false.
- search-list-condition (one, optional): (New in
v4m4) Condition to be used for listing the selectable elements
that can be used as a reference.
- on-change-search (one, optional): (New in
v2.2.5) Allows you to specify your own action for searching
when the user type a new key.
- search-action (one, optional): Allows you to
specify your own action for searching when the user click on search
link.
- descriptions-list: Display the data as a list of
descriptions, typically as a combo. Useful when there are few elements
of the referenced object.
- action (several, optional): (New in v2.0.1)
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-view/> case.
If you do not use
<reference-view/> OpenXava draws a
reference using the default view. For example, if you have a reference
like this:
<entity>
...
<reference name="family" model="Family" required="true"/>
...
</entity>
The user interface will look like this (modify link
new in v2.0.4):
Choose
view
The most simple customization is to specify the view of the referenced
object that you want to use:
<reference-view reference="invoice" view="Simple"/>
In the Invoice component you must have a view named Simple:
<component name="Invoice">
...
<view name="Simple">
<members>
year, number, date, yearDiscount;
</members>
</view>
...
</component>
Thus, instead of using the default view of
Invoice (that shows
all invoice data) OpenXava will use the next one:
Customizing
frame
If you combine
frame="false" with group you can group visually a
property that is not a part of a reference with that reference, for
example:
<reference-view reference="seller" frame="false"/>
<members>
...
<group name="seller">
seller;
relationWithSeller;
</group>
...
</members>
And the result:
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
<search-action/>
in this way:
<reference-view reference="seller">
<search-action action="MyReference.search"/>
</reference-view>
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"/> <!-- Not needed since v4m2 -->
<use-object name="xava_referenceSubview"/> <!-- Not needed since v4m2 -->
<use-object name="xava_tab"/> <!-- Not needed since v4m2 -->
<use-object name="xava_currentReferenceLabel"/> <!-- Not needed since v4m2 -->
</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
controllers
chapter.
Custom
creation action
If you do not write
create="false" 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"/> <!-- Not needed since v4m2 -->
</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 (new in v2.0.4)
If you do not write
modify="false" 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"/> <!-- Not needed since v4m2 -->
</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
<descriptions-list/> 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:
<descriptions-list
description-property="property" <!-- 1 -->
description-properties="properties" <!-- 2 -->
depends="depends" <!-- 3 -->
condition="condition" <!-- 4 -->
order-by-key="true|false" <!-- 5 -->
order="order" <!-- 6 -->
filter="filter class" <!-- 7 New in v6.4 -->
label-format="NORMAL|SMALL|NO_LABEL" <!-- 8 -->
show-reference-view="true|false" <!-- 9 New in v5.5 -->
for-tabs="tab1,tab2,..." <!-- 10 New in v4m4 -->
not-for-tabs="tab1,tab2,..." <!-- 11 New in v4m4 -->
label-style="style" <!-- 12 New in v5.8 -->
/>
- description-property (optional): The property 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.
- description-properties (optional): As description-property
(and excluding with it) but allows to set more than one property
separated by commas. To the final user the values are concatenated.
- 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).
- condition (optional): Allows to specify a condition
(with SQL style) to filter the values that are shown in the
description list.
- order-by-key (optional): By default the data is
ordered by description, but if you set this property to true it will
be ordered by key.
- order (optional): Allows to specify an order (with
SQL style) for the values that are shown in the description list.
- filter (optional): (New in v6.4) Allows to define the
logic to fill the values of the parameters used in the condition (the
?). It must implement IFilter
and you can use here the same
filters used for <tab>.
- label-format (optional): Format to display the
label of the reference. See label-format
for properties.
- show-reference-view (optional): (New in v5.5)
Shows a combo and a detail view of the reference at the same time. The
reference view is read only and its value changed when the combo is
changed by the user. The view used is the one specified in <reference-view
view="">. The default value is false.
- for-tabs (optional): (New in v4m4) Allows
to specify multiple tab names separated by commas. If any of the
properties fromdescriptionProperties is in any of these tabs
the filter part will be shown with a description list.
- not-for-tabs (optional): (New in v4m4)
Allows to specify multiple tab names separated by commas. If any of
the properties fromdescriptionProperties is in any of these
tabs the filter part will continue to be displayed like a plan
property.
- label-style (optional): (New in v5.8):
Style to display the label of this property. It has the predefined
styles 'bold-label', 'italic-label' and 'reverse-label'; though you can use your own custom style if you
define it in web/xava/style/custom.css.
The most simple usage is:
<reference-view reference="warehouse">
<descriptions-list/>
</reference-view>
That displays a reference to warehouse in this way:
In this case it shows all warehouses, although in reality it uses the
base-condition
and the filter specified in the default tab of
Warehouse. You
will see more about
tabs in
its chapter .
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:
<reference-view reference="family">
<descriptions-list order-by-key="true"/> <!-- 1 -->
</reference-view>
<reference-view reference="subfamily" create="false"> <!-- 2 -->
<descriptions-list
description-property="description" <!-- 3 -->
depends="family" <!-- 4 -->
condition="${family.number} = ?"/> <!-- 5 -->
order="${description} desc"/> <!-- 6 -->
</reference-view>
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:
<reference-view reference="alternateSeller" read-only="true">
<descriptions-list description-properties="level.description, name"/>
</reference-view>
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 set
read-only="true" in a reference as
descriptions-list,
then the description (in this case
level.description +
name)
is displayed as a simple text property instead of using a combo.
Reference
search on change event (new in v2.2.5)
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
on-change-search,
just in this way:
<reference-view reference="subfamily">
<on-change-search class="org.openxava.test.actions.OnChangeSubfamilySearchAction"/>
</reference-view>
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 {
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 behavior 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.
Choosing
an editor (reference, new in v3.1.3)
An editor display the reference to the user and allows him to edit its
value. By default, the editor OpenXava uses for references is a detailed
view inside a frame (the standard way) or a combo (if you use
<descriptions-list/>,
but you can specify your own editor to display a concrete reference inside
a view with <
reference-view ... editor= />.
For example, if you have a reference to a component
Color and
you want to display it in some particular view using a custom user
interface, such as a radio buttons group with available colors. Just can
do it in this way:
<reference-view reference="color" editor="ColorRadioButtons"/>
In this case the
ColorRadioButtons editor will be used for
displaying/editing, instead of the default one. You must define your
ColorRadioButton
editor in the
xava/editors.xml file of your project:
<editor name="ColorRadioButtons" url="colorRadioButtonsEditor.jsp"/>
Also you have to write the JSP code for your editor in
colorRadioButonsEditor.jsp.
This feature is for changing the editor for a concrete reference in a
concrete view of a component entity. If you want to change the editor for
all references to certain entity type at application level then it's
better to configure it using
xava/editors.xml file.
Learn more on
Editors
for references section of
chapter
9.
Collection
view
Suitable to refine the collection presentation. Here is its syntax:
<collection-view
collection="collection" <!-- 1 -->
view="view" <!-- 2 -->
read-only="true|false" <!-- 3 -->
edit-only="true|false" <!-- 4 -->
create-reference="true|false" <!-- 5 -->
modify-reference="true|false" <!-- 6 new in v2.0.4 -->
as-aggregate="true|false" <!-- 7 new in v2.0.2 -->
editor="editorName" <!-- 8 new in v3.1.3 -->
collapsed="true|false" <!-- 9 new in v5.0 -->
>
<list-properties ... /> <!-- 10 -->
<search-list-condition ... /> <!-- 11 new in v4m4 -->
<row-style ... /> <!-- 12 new in v2.2.2 -->
<edit-action ... /> <!-- 13 -->
<view-action ... /> <!-- 14 -->
<new-action ... /> <!-- 15 new in v2.0.2 -->
<add-action ... /> <!-- 16 new in v5.7 -->
<save-action ... /> <!-- 17 new in v2.0.2 -->
<hide-detail-action ... /> <!-- 18 new in v2.0.2 -->
<remove-action ... /> <!-- 19 new in v2.0.2 -->
<remove-selected-action ... /> <!-- 20 new in v2.1 -->
<list-action ... /> ... <!-- 21 -->
<row-action ... /> ... <!-- 22 new in v4.6 -->
<detail-action ... /> ... <!-- 23 -->
<on-select-element-action ... /> <!-- 24 new in v3.1.2 -->
</collection-view>
- collection (required): The look of the collection
with this name will be customized.
- view (optional): The view of the referenced object
(each collection element) which is used to display the detail. By
default the default view is used.
- read-only (optional): By default false; if you set
it to true, then the final user only can view collection elements, he
cannot add, delete or modify elements.
- edit-only (optional): By default false; if you set
it to true, then the final user can modify existing elements, but not
add or remove collection elements.
- create-reference (optional): By default true, if
you set it to false then the final user doesn't get the link to create
new objects of the referenced object type. This only applies in the
case of an entity references collection.
- modify-reference (optional): (New in v2.0.4)
By default true, if you set it to false then the final user doesn't
get the link to modify the objects of the referenced object type. This
only applies in the case of an entity references collection.
- as-aggregate (optional): (New in v2.0.2)
By default false. By default the collections of aggregates allow the
users to create and to edit elements, while the collections of
entities allow only to choose existing entities to add to (or remove
from) the collection. If you put as-aggregate to true then
the collection of entities behaves as a collection of aggregates,
allowing to the user to add objects and editing them directly. It has
no effect in case of collections of aggregates.
- editor (optional): (New in v3.1.3) Name
of the editor to use for displaying the collection in this view. The
editor must be declared in OpenXava/xava/default-editors.xml
or xava/editors.xml of your project.
- collapsed (optional): (New in v5.0) The
collection will be shown collapsed. Visually this means that the frame
surrounding the collection view will be initially closed. Later the
user will be able to set his preferences by clicking on the expansion
icon. The default value is false.
- list-properties (one, optional): 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).
The suffix + (new in v4.1) can be added to a property name
to show the sum
of the column at bottom, like in the tabs. Before v5.9 the
column summation was not allowed in calculated collections. Summation
(+) for calculated properties in not calculated collections is not
allowed.
- search-list-condition (one, optional): (New in
v4m4) Condition to be used for listing the selectable elements
that can be added to the collection. It does not apply to aggregate
collections.
- row-style (several, optional): (New in v2.2.2)
To give a special style to some rows. Behaves equals that in the Tab case. It does not works for
calculated collections.
- edit-action (one, optional): 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.
- view-action (one, optional): 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.
- new-action (one, optional): (New in v2.0.2)
Allows you to define your custom action to start creating a new
element and insert it into the collection. This is the action executed
on click in 'New' link. Before
v5.7 this was also used to define the 'Add' action because then
'New' and 'Add' did not coexist.
- add-action (one, optional): (New in v5.7)
Allows you to define your custom action to start adding a new element
to the collection choosing from an existing one. This is the action
executed on click in 'Add' link.
- save-action (one, optional): (New in v2.0.2)
Allows you to define your custom action to save the collection
element. This is the action executed on click in 'Save detail' link.
- hide-detail-action (one, optional): (New in
v2.0.2) Allows you to define your custom action to hide the
detail view. This is the action executed on click in 'Close' link.
- remove-action (one, optional): (New in v2.0.2)
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.
- remove-selected-action (one, optional): (New
in v2.1) 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.
- list-action (several, optional): To add actions in
list mode; usually actions which scope is the entire collection.
- row-action (several, optional): (New in v4.6)
To add actions in list mode in each row.
- detail-action (several, optional): To add actions
in detail mode, usually actions which scope is the detail that is
being edited.
- on-select-element-action (one, optional): (New
in v3.1.2) To define an action to be executed when an element
of the collection is selected or unselected.
If you do not use
<collection-view/>, 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:
<collection-view collection="customers" view="Simple">
<list-properties>
number, name, remarks, relationWithSeller, seller.level.description
</list-properties>
</collection-view>
And the collection is displayed:
You see how you can put qualified properties into the properties list (as
seller.level.description).
When the user clicks on 'Edit', then the view
Simple of
Customer
will be rendered; for this you must have defined a view called
Simple
in the
Customer component (the model of the collection
elements).
This view is also used if the user click on 'Add' in a collection of
aggregates, but in the case of a collection of entities OpenXava does not
show this view, instead it shown a list of entities to add
(new in
v2.2).
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:
Custom
edit/view action
You can refine easily the behavior when the 'Edit' link is clicked:
<collection-view collection="details">
<edit-action action="Invoices.editDetail"/>
</collection-view>
You have to define
Invoices.editDetail in
controllers.xml:
<controller name="Invoices">
...
<action name="editDetail" hidden="true"
class="org.openxava.test.actions.EditInvoiceDetailAction">
<use-object name="xava_view"/> <!-- Not needed since v4m2 -->
</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
(new
in v2.2.1), in this way:
<collection-view collection="details">
<edit-action action=""/>
</collection-view>
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
read-only.
The technique to refine the view action (the action for each row, if the
collection is read only) is the same but using
<view-action/>
instead of
<edit-action/>.
Custom
list and row action actions
Use
<list-action /> to define actions that applies to
entire collection and
<row-action /> (new in v4.6) to
define actions for each row.
<list-action /> and
<row-action
/> are very alike, indeed they are coded in the same way, the
difference is that the
<list-action /> actions are shown
in the button bar while the
<row-action /> actions are
shown in each row.
<list-action /> actions can be also
shown in each row if they are defined as
in-each-row="true" in
controllers.xml.
This is an example:
<collection-view collection="fellowCarriers" view="Simple">
<list-action action="Carriers.translateName"/>
</collection-view>
Now a new link is shown to user:
And also you see that there is a check box in each row (since v2.1.4 check
box is always present in all collections).
Also you need to define the action in controllers.xml:
<controller name="Carriers">
<action name="translateName"
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()) {
ICarrier carrier = (ICarrier) 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). In the case of
<row-action
/>, getSelectedObjects() and
getMapsSelectedValues()
return an unique element, the one of the row of the action, even if the
row is not checked.
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
(new in v2.1.4).
Default
list and row actions (new in v2.1.4)
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
<list-action /> but it applies
to all collections at once.
If you want to define default row actions (<
row-action />)
you can define the controller
DefaultRowActionsForCollections (new in
v4.6).
This feature does not apply to calculated collections
(new in v2.2.1).
Custom
detail actions
Also you can add your custom actions to the detail view used for editing
each element. These actions are applicable only to one element of
collection. For example:
<collection-view collection="details">
<detail-action action="Invoices.viewProduct"/>
</collection-view>
In this way the user has another link to click in the detail of the
collection element:
You need to define the action in controllers.xml:
<controller name="Invoices">
...
<action name="viewProduct" hidden="true"
class="org.openxava.test.actions.ViewProductFromInvoiceDetailAction">
<use-object name="xava_view"/> <!-- Not needed since v4m2 -->
<use-object name="xavatest_invoiceValues"/> <!-- Not needed since v4m2 -->
</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 {
@Inject // Since v4m2
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
controllers
chapter 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
(new in
v2.0.2) with
addDetailAction(),
removeDetailAction(),
addListAction() and
removeListAction(), see API doc for
org.openxava.view.View.
Refining
collection view default behavior (new in v2.0.2)
Using
<new-action/>,
<add-action/> (new in
v5.7),
<save-action/>,
<hide-detail-action/>,
<remove-action/> and
<remove-selected-action/>
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:
<collection-view collection="details">
<save-action action="DeliveryDetails.save"/>
</collection-view>
You must have an action
DeliveryDetails.save in your
controllers.xml:
<controller name="DeliveryDetails">
<action name="save"
class="org.openxava.test.actions.SaveDeliveryDetailAction">
<use-object name="xava_view"/> <!-- Not needed since v4m2 -->
</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.
New in v2.2.1: Also it's possible to remove any of these actions
from User Interface, for example, you can define a
<collection-view/>
in this way:
<collection-view collection="details">
<remove-selected-action action=""/>
</collection-view>
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.
Action
when a collection element is selected (new in v3.1.2)
You can define an action to be executed when a collection element is
selected or unselected. This is accomplished using
<on-select-element-action/>
inside
<collection-view/>. For example, imagine that you
have a collection just like this one:
And you want that when a row is selected the value of the
selectedIngredientSize
field in the user interface will be updated. Let's code it.
First you have to declare the action in
<collection-view/>:
<collection-view collection="ingredients">
<on-select-element-action action="Formula.onSelectIngredient"/>
</collection-view>
In this simple way (1) you say that when the user clicks on the checkbox
of the collection row the
Formula.onSelectIngredient action will
be executed. This action is declared in
controllers.xml, in this
way:
<controller name="Formula">
...
<action name="onSelectIngredient" hidden="true"
class="org.openxava.test.actions.OnSelectIngredientAction">
<use-object name="xava_view"/> <!-- Not needed since v4m2 -->
</action>
...
</controller>
Now, only remains the code of the
OnSelectIngredientAction
class:
public class OnSelectIngredientAction extends OnSelectElementBaseAction { // 1
public void execute() throws Exception {
int size = getView().getValueInt("selectedIngredientSize");
size = isSelected() ? size + 1 : size - 1; // 2
getView().setValue("selectedIngredientSize", new Integer(size));
}
}
The easiest way to implement the action is extending from
OnSelectElementBaseAction, this allows you to
access to the property
selected (by means of
isSelected(),
2) that indicates wheter the user has selected or unselected the row; and
row (using
getRow()) that indicates the row number of
the affected collection element.
Choosing
an editor (collection, new in v3.1.3)
An editor display the collection to the user and allows him to edit its
values. By default, the editor OpenXava uses for collections is a list
that shows the data in a tabular fashion, allowing to order, filter and
paging, but you can specify your own editor for display a concrete
collection using
<collection-view ... editor= />.
For example, if you have a collection of
Customer entities and
you want to display it in some particular view of a component using a
custom user interface, such as a simple list of names. Just can do it in
this way:
<collection-view collection="customers" editor="CustomersNames"/>
In this case the
CustomersNames editor will be used for
displaying/editing, instead of the default one. You must define your
CustomersNames
editor in the
xava/editors.xml file of your project:
<editor name="CustomersNames" url="customersNamesEditor.jsp"/>
Also you have to write the JSP code for your editor in
customersNamesEditor.jsp.
This feature is for changing the editor for a concrete collection in a
concrete view of a component only. If you want to change the editor for
all collections to certain entity type at application level then it's
better to configure it using
xava/editors.xml file.
Learn more on
Editors
for collections section of
chapter
9.
View
property
With
<property/> within
<view/> you define
a property that is not in the model 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:
<view>
<property name="deliveredBy">
<valid-values>
<valid-value value="employee"/>
<valid-value value="carrier"/>
</valid-values>
<default-value-calculator
class="org.openxava.calculators.IntegerCalculator">
<set property="value" value="0"/>
</default-value-calculator>
</property>
<property-view property="deliveredBy">
<on-change class="org.openxava.test.actions.OnChangeDeliveryByAction"/>
</property-view>
...
</view>
You can see that the syntax is exactly the same as in the case of a
property of a model; you can even use
<valid-values/> and
<default-value-calculator/>. After defining the property
you can use it in the view as usual, for example with
on-change
or putting it in members.
View
actions (new in v2.0.3)
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 action element, in this way:
<members>
number;
type;
name, <action action="Customers.changeNameLabel"/>;
...
</members>
The visual effect will be:
You can see the link 'Change name label' that will execute the action
Customers.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 the attribute
always-enabled,
as following:
<action action="Customers.changeNameLabel" always-enabled="true"/>
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 action
element may be useful.
See more about actions in
controllers
chapter.
Transient
component: Only for creating views (new in v2.1.3)
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 component,
declare it as transient
(new in v2.1.3) and define your view
from it.
An transient component 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:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE component SYSTEM "dtds/component.dtd">
<!--
Example of an OpenXava transient component (not persistent).
This can be used, for example, to display a dialog,
or any other graphical interface.
-->
<component name="FilterBySubfamily">
<entity>
<reference name="subfamily" model="Subfamily2" required="true"/>
</entity>
<view name="Family1">
<reference-view reference="subfamily" create="false">
<descriptions-list condition="${family.number} = 1"/>
</reference-view>
</view>
<view name="Family2">
<reference-view reference="subfamily" create="false">
<descriptions-list condition="${family.number} = 2"/>
</reference-view>
</view>
<view name="WithSubfamilyForm">
<reference-view reference="subfamily" search="false"/>
</view>
<transient/> <!-- 1 -->
</component>
For defining a component as transient you only need to put
<transient/>
at the end of the component definition (1), just in the part for the
mappings. You mustn't put the mapping 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.