User Guide

Introduction

GuidATE is a system of adapters for the ZK web framework that simplify the use of this framework with ApplicATE and DominATE.

GuidATE follows the classic MVC principle. Imagine your ApplicATE services as your model and create views using ZK zul pages, then bind them together so that anywhere something changes the other side of the bind changes too.

Suppose, for example, that you have an ApplicATE command with two properties, say propertyA and propertyB and imagine that propertyB value changes when propertyA is set.

Suppose now that you have a zul page bound to this service with two text boxes bound to propertyA and propertyB respectively. If the user types something in the former text box, propertyA is set, propertyB changes and fires a PropertyChangeEvent. The latter text box listen to propertyB event and update its content.

Back beans

The main concept behind GuidATE binding implementation is that of "back bean".

A "back bean" is an object, typically an ApplicATE service, bound to a container ZK component like a window, a vbox (or hbox), etc. The binding is performed applying the BackBeanComposer to the component and specifying the back bean with the "backBean" custom attribute, like in the following example:

<window apply="it.amattioli.guidate.containers.BackBeanComposer">
        <custom-attributes backBean="${myService}"/>
        
        ...
        
</window>

The "backBean" custom attribute can be specified in two ways:

  • You can directly specify the object to be bound, like in the example
  • You can specify a String. In this case the real back bean will be created calling the createService method on the ApplicateSession passing in the specified string. The ApplicateSession subclass and service factory used can be specified in the GuidATE configuration as described in the Configuration section.

If you have a component inside this window, i.e. a button, you can find its back bean using the BackBeans utility class:

Object backBean = BackBeans.findBackBean(comp);

where comp is a reference to the button. The findBackBean method will look upside the parent-child components structure until it find a container component bound to a back bean.

If you are interested in this container instead of the back bean, use the findContainer method:

 Component container = BackBeans.findContainer(comp);

If you are inside an event handler you can look for the event target back bean:

public void onClick(Event evt) {
    Object backBean = BackBeans.findTargetBackBean(evt);

    ...

}

Properties

Inside a back bean bound container you can put property bound components.

Non-editable properties

The simplest property component is "propertyValue". This is a label that shows the value of a property of the back bean. The label is bound to the property so as to update its content if the back bean fires a PropertyChangeEvent for the bound property.

Like all the other property bound components, a "propertyValue" component can be specified in two ways:

  • applying a composer to a standard ZK component
  • using a custom component

so the following two are equivalent:

<label apply="it.amattioli.guidate.properties.LabelPropertyComposer">
        <custom-attributes propertyName="myProperty"/>
</label>
<propertyValue propertyName="myProperty"/>

The "propertyName" attribute is the name of the property of the back bean object you are binding to. You can specify:

  • simple properties, i.e. propertyName="myProperty"
  • nested properties, i.e. propertyName="myProperty.hisProperty"
  • indexed properties, i.e. propertyName="myProperty[5]"
  • mapped properties, i.e. propertyName="myProperty(aStringValue)"

    Refer to Apache Commons BeanUtils documentation for details.

    Property bound composers do type conversion too. They guess the right converter using the property class but you can specify your own type converter using the "typeConverter" attribute:

    <propertyValue propertyName="myProperty" type="it.amattioli.myProject.myTypeConverter"/>
    

    Refer to the Type conversion section for details.

    For properties whose class is a collection you can use the "propertyGrid" component. It shows a grid with a row for each element of the collection. Obviously every time the back bean fires a PropertyChangeEvent for the collection the grid is update.

    The two forms of this component are:

    <grid apply="it.amattioli.guidate.properties.CollectionPropertyComposer">
        <custom-attributes propertyName="collectionProperty"/>
    </grid>
    
    <propertyGrid propertyName="collectionProperty"/>
    

    If you don't specify anything inside the "propertyGrid" component, like in the latter example, the grid will have one column without header and the content will be determined using the same type conversion algorithm used for the propertyValue component.

    If you need a more complex behavior you can specify columns and a row prototype:

    <propertyGrid propertyName="collectionProperty">
        <columns>
                <column width="50%" label="Description"/>
                <column width="50%" label="Q.ty" align="right"/>
        </columns>
        <rows>
                <rowPrototype>
                <propertyValue propertyName="description" width="90%"/>
                <propertyValue propertyName="quantity" width="90%"/>
            </rowPrototype>
        </rows>
    </propertyGrid>
    

    The rowPrototype will assign each element of the collection as the back bean of the corresponding row. In this way the propertyValue component will be bound to a property of the collection element.

    Following this principle you can even nest property grids using the master detail feature of the ZK grid component:

    <propertyGrid propertyName="collectionProperty">
        <columns>
            <column width="10%"/>
                <column width="40%" label="Description"/>
                <column width="40%" label="Price" align="right"/>
        </columns>
        <rows>
                <rowPrototype>
                    <detail>
                    <propertyGrid propertyName="nestedCollection">
                        <columns>
                            <column width="50%" label="Description"/>
                                <column width="50%" label="Q.ty" align="right"/>
                            </columns>
                            <rows>
                                <rowPrototype>
                                    <propertyValue propertyName="description" width="90%"/>
                                <propertyValue propertyName="quantity" width="90%"/>
                                </rowPrototype>
                            </rows>
                    </propertyGrid>
                </detail>
                <propertyValue propertyName="description" width="90%"/>
                <propertyValue propertyName="price" width="90%"/>
            </rowPrototype>
        </rows>
    </propertyGrid>
    

    There are situations where informations can better be described by an image rather than words. For example we can show a boolean value with two alternate images. In this cases we can use the "imageProperty" composer:

    <imageProperty propertyName="booleanProperty">
        <valueImage src="img/true.gif" value="${true}"/>
        <valueImage src="img/false.gif" value="${false}"/>
    </imageProperty>
    

    Generally speaking the imageProperty composer can be used when you have a property that assumes a small set of predefined values like a boolean or an enumeration. You have to give a "valueImage" for each possible value the property can assume. The associated value must be indicated in the value attribute of the valueImage.

Editable properties

Properties can be edited too. To do this for a String property we can use a "textProperty" component:

<textProperty propertyName="myProperty"/>

Now the binding is bidirectional. Like for "propertyValue" the component is updated when the property value changes but, if the user edit it, when the focus leaves the component the property is set.

The component does validation on the property too. At this purpose it uses the DominATE validation facility. If the back bean implements the Validator interface the component uses its methods to validate the property value before setting it. The value is set in the back bean only if validation succeeds.

If the back bean does not implement the Validator interface a DefaultValidator is used.

A special case is represented by the @Length Hibernate Validator annotation. If you annotate your property with it, like in the following snippet:

@Length(max=20)
public String getMyProperty() {
    return myProperty;
}

a textProperty component bound to this property will have the maxlength attribute set to the maximum length specified in the annotation so the user will not be able to digit more than the given number of characters.

For properties of other types you have similar components:

  • intProperty
  • dateProperty
  • decimalProperty
  • timeIntervalProperty

The checkProperty component shows a checkbox bound to a boolean property in the back bean. When the property is true the checkbox is checked, otherwise it is unchecked.

The listProperty component is more complicated. Generally speaking it shows a listbox but we need to distinguish various cases.

If the bound property is single-valued, like:

public MyEntity getMyProperty() {
    return myProperty;
}

public void setMyProperty(MyEntity value) {
    myProperty = value;
}

the listbox will allow the user to select a value from the list. Every time the user select an item in the list the corresponding value will be set in the back bean.

If the bound property is a collection, like:

public Collection<MyEntity> getMyProperty() {
    return myProperty;
}

public void setMyProperty(Collection<MyEntity> values) {
    myProperty = values;
}

the listbox will be a multi-selection one. Every time the user select an item in the list the entire set of the selected items will be set in the back bean.

The algorithm used to determine the list of items to display is the following:

  • If in the back bean there is a getMyPropertyValues() method (where MyProperty must be replaced with your property name), this method will be called to retrieve the item list
  • If the base property class is an entity, the entity repository will be used to retrieve all the possible entities
  • If the base property class is an enum, all the enum values will be used

    If the property is single-valued and is not annotated with @NotNull Hibernate Validator annotation an empty row will be added to the top of the list to represent the null value.

Browsing views

List browsing

If your back bean is an object that implements the ListBrowser interface you can use the "browserListbox" component to show its content.

Basically it is a ZK listbox. Its two forms are:

<listbox apply="it.amattioli.guidate.browsing.BrowserListboxComposer">
   ......
</listbox>
<browserListbox>
   ......
</browserListbox>

The items that will be shown will be each element of the list obtained calling the getList() method on the back bean.

The listbox should have a listhead section and a listitemPrototype. The listitemPrototype is similar in purpose to the rowPrototype we saw for the propertyGrid: it works like a prototype for each listitem that will be visible in the listbox.

A complete example of browserListbox is:

<browserListbox width="99%">
    <listhead>
        <listheader label="Name"/>
        <listheader label="Surname"/>
        <listheader label="Birth date"/>
    </listhead>
    <listitemPrototype>
        <labelListcell propertyName="name"/>
        <labelListcell propertyName="surname"/>
        <labelListcell propertyName="birthdate"/>
    </listitemPrototype>
</browserListbox>

The "labelListCell" component is similar to the "propertyValue" one. If you prefer you can even use a standard ZK listcell with one or more propertyValue inside it:

<browserListbox width="99%">
    <listhead>
        <listheader label="Full Name"/>
        <listheader label="Birth date"/>
    </listhead>
    <listitemPrototype>
        <listcell>
            <propertyValue propertyName="name"/>
            <propertyValue propertyName="surname"/>
        </listcell>
        <labelListcell propertyName="birthdate"/>
    </listitemPrototype>
</browserListbox>

When the user select a row in the listbox the corresponding object in the browser is selected too. Using getSelectedObject() in the browser allow you to retrieve this object.

Instead of the standard ZK "listheader" component you can use the browserListheader one. This component allows automatic ordering of the browser content (calling the setOrder() method on the browser) each time the user clicks on the column header. To specify the property name to be passed to the setOrder() method you have two options:

  • if you used a labelListcell component for the corresponding cell in the prototype the property name is retrieved from its propertyName attribute
  • if you are not using a labelListcell or if you want to override what's in the labelListcell propertyName attribute you can specify an orderColumn attribute in the browserListHeader

Here is an example:

<browserListbox width="99%">
    <listhead>
        <browserListheader label="Full Name" orderColumn="name"/>
        <browserListheader label="Birth date"/>
    </listhead>
    <listitemPrototype>
        <listcell>
            <propertyValue propertyName="name"/>
            <propertyValue propertyName="surname"/>
        </listcell>
        <labelListcell propertyName="birthdate"/>
    </listitemPrototype>
</browserListbox>

Tree browsing

To show the content of a TreeBrowser you can use a standard ZK tree component with a BrowserTreeComposer applied to it.

Obviously

Browsing tools

Command views

CommandComposer

Validation

Editors

Configuration

Type conversion