home
products
education
support
partners
company

introduction

get started

articles

manual

tutorials

reference docs

case studies

knowledge base

goodies

screenshots

demos

echoes

Carousel zone


SourceForge.net Logo

home

forums

downloads

bugs

mailing list
XUI Zone - manual

number of ratings 0
average rating 0.00

14 Data binding

Carousel applicatiosn use the Model-View-Controller (MVC) pattern to separate the key concerns in building an application. A key principal of this separation is that the model can be implemented without overdue concern for the presentation layer or user interface. Keeping the model simple makes it easy to implement business logic.

The separation of the model also means that the business logic need have little concern for the modalities or peculiarities of the user interface or the user interaction within the application.

Of course the model cannot be completely divorced from the user interface as occasionally interaction is required, say for instance if the model requires special input or needs to signal an error. Carousel achieves the separation of concerns espoused by the MVC architecture by implementing a loose coupling between the data model and the user interface.

On the data or model side of the architecture Carousel provides a rich data model that can be addressed via XML and XPath like references. This model can be composed of a wide variety of data and the data can be used in a variety of ways depending on the needs of the user interface and application.

Typically the data model will consist of data from a number of data sources such as static data defined in flat files, tables mapped in from a database, server side data obtained via service calls and configuration information.

Carousel also saves its own data to the model, for example saving the component state data to the xui_state node so that things like list selections indices can be accessed via the mode or used for master details linking.

Your application may also save data to the model as it needs. The model grows with your application and its content is highly configurable. For example each node within the model can have multiple child nodes and each node can be a rich type, not just the basic types built into Java. Tables themselves are good examples of these composite objects, consisting of multiple rows and with rows consisting of multiple fields.

The diagram below shows how various data sources might be mapped into the hierarchy and how an individual object can be a composite of other objects. The diagram also serves to show the hierarchical arrangement of data which we will see being used in the next section on data binding.

Data binding

Carousel uses a data binding technique that allows components to abstractly reference data in the model. This data can be simple scalar values or more complex types like lists and tables. From the UI perspective all that needs to be known is where the data resides in the model. Once the location has been declared Carousel takes care of getting and saving the data via data binding objects which are implicitly constructed as your pages are loaded.

An example binding is shown below.

Code Sample 14-1 - Simple data binding

< Data >

< Bind name= "ageCombo" source= "customers/ageRanges" />

< Bind name= "firstNameEdit" source= "firstname" />

< /Data >

In the above example two user interface components are bound to data. The components are named by the name attribute while the location of the data is referenced by the source attribute.

These bindings cause the named UI components to be filled with data from the source nodes whenever the page containing the components is displayed. In this specific example the ageCombo is filled with items from the customer/ageRanges node within the data model, while the firstnameEdit is filled with data from the firstname model node.

Even if the model hasn't been populated Carousel will automatically create storage for the data. So, if for instance the user interface component is an input component like an edit field Carousel will create a node in the model to hold the user input. In this way the model can be subsequently referenced and the user interface value can be retrieved without knowing how the input was obtained. Similarly if the bound storage is updated or modified then the user input field will display the new value whenever it is redisplayed. Furthermore, the same model node can be bound to multiple user interface components so that the same value can be displayed across multiple pages or in different contexts.

A simple static data binding

The simplest form of data that can be bound to the user interface is a static binding. A static binding is a binding that binds static or read-only data to a user interface component. Static data is often used for population of drop-down lists, labels and default values.

While it may seem like extra work to use data bindings for simple read-only data rather than hard coding the data it is often useful as the data is contained in a separate file from the source code. At its simplest this separation means that the application will be a little easier to maintain as updates to code are possible without risk to the application code (thus minimizing testing requirements).

At a more sophisticated level the separation can assist in branding of applications as the static data can be easily swapped for other data. Other features are enabled by the mapping of data used for static bindings including localization and customization of things like dimensional units, however these topics are beyond the scope of the current chapter and will be touched upon in later chapters.

Configuring the data source

So how does Carousel get the data in the first place? The data source used by Carousel is automatically created and configured whenever a new application is configured, but it is worth highlighting the various steps and processes used to locate the data.

Upon startup the startup.properties file of the application is accessed and the ModelData parameter is checked. This parameter points to the data model configuration file.

Code Sample 14-2 - The data source configuration reference in the startup file

ModelData=simpledatasets.xml

The configuration file is loaded by the application and processed as it contains a list of data sources. There may be one or more data sources depending on the needs of your application, but again this two stage configuration adds flexibility by allowing otherwise disparate data sources to be cleanly mapped into the data model. For examples, the specification required for database access differs considerably from the simple setup required for static data. The example below shows one such very simple configuration file.

Code Sample 14-3 - The data source configuration file (simpledataset.xml)

The file simply points of the another file which contains the static data. In a more complete application there may be several datasources of varying types and each would be listed in the data sources configuration file as a DataSource entry.

The actual data file is also relatively simple. The important attributes to note are the id attribute and the value attribute.

Code Sample 14-4 - The static data file (simpledata.xml)

The id field is important as it is this attribute that is used to identify a node in the model. The path used to access an individual node is just the list of all the node id s back to the root node separated by the / character. This concept should be familiar to anyone who has used a filesystem being vary like the paths that operating systems use.

As mentioned above, these configuration files are setup by Carousel and Carousel also provides editing facilities so you will rarely need to interact with the files directly.

The files and paths to the configuration files can also be chosen as part of the project settings.

How the bindings work

The data bindings work on the simple principle that any change made by the user to the user interface component will cause the associated model node to be updated and made consistent.

Similarly any change to the model will be propagated back to any user interface components that are bound to the model node. Carousel uses some extra steps to eliminate unnecessary work and unnecessary updates but the key update occurs during page transitions and these are discussed in a later section ( "Update on page display" on page 137 ).

When the model is updated one possibility would be to dynamically update the user interface to reflect the latest data, however this would quickly get out of hand if multiple nodes were updated. Since there is no obvious point at which we can say that an internal (to the application) is complete the update from the model side is not as aggressive as the saving of user interface data and more is left to the discretion of the programmer.

From the user interface side the bindings listen for any user input or change in state. Each binding is aware of the type of input events that affect its data and add the appropriate listeners. As these listeners are triggered they save the data to the model.

To accommodate the differences between user interface components and the types of updates they require Carousel includes a range of binding types and adapters that can act as intermediaries between bindings and the various types of model node. All of these bindings share a common superclass and form a hierarchy.

Carousel constructs the appropriate bindings by mapping the model data node or data source to the target user interface component. To reiterate the way in which bindings are setup In XML we again show a binding for an edit field:

Code Sample 14-5 - A simple data binding for an edit field

For more complex data types a one to one relationship may not exist between the data type and the user interface component. Indeed there may not be a single data binding type that can perform such binding so Carousel allows chaining of data bindings and the use of adapters. For example, in the case of a type such as a table it would not be realistic to try and display a table in an edit field yet we might want to display a particular field value. With the aforementioned chaining Carousel can handle such situations and fortunately it can even set up these bindings for you automatically.

The point of all of this is that in using bindings you are not constrained to using simple types or simple relationships, you can build and use a wide variety of data.

Separating data from state

We started by considering the binding of static data as read-only data. Since the data source is read-only it is not possible to save the modified data to the same node and therefore Carousel creates a new node for the data by appending the source path to the ' xui_state ' node. Thus where the above example sourced its data from the ' person/name ' node the result is saved to ' xui_state/person/name '.

Some controls such as drop-down lists produce selection information (such as the index of the selected item) that do not fit well with the input data (the items it the list), particularly if the input data is considered static or read-only. Again this data is saved to the ' xui_state ' node.

For the most part you need not be aware of the output node as it is an internal detail of the bindings. In the above example the output path was not specified as Carousel automatically configures the path by appending the source path to the xui_state node to give an output path of xui_state/person/name in the case of the last example.

Setting up a binding

Setting up a binding in XML is pretty straightforward as has been shown above. The bindings are specified in the Data section of the page:

Code Sample 14-6 - Some more bindings

...

While Carousel setups the appropriate binding if a page is constructed from XML the story in Java is a little more complicated as the type of binding must be chosen explicitly:

Code Sample 14-7 - Adding a binding in Java

addBinding( new XTextBinding( firstNameEdit, "person/firstName" ));

addBinding( new XTextBinding( secondNameEdit, "person/secondName" ));

In this example we know that the bound user-interface components are edit fields and therefore we use the XTextBinding binding type. Some of the bindings provided by Carousel are:

Table 14-1 - Some data binding types

Binding

Usage

XLabelBinding

A simple binding that is used for read-only Label components

XTextBinding

Bind a TextComponent to a data model value/node. The binding allows a model node to linked to a UI component so that it can be refreshed when new data is written to the model or conversely when the UI component needs to write data to the model.

This binding is designed to be used by components such as Edit fields, TextComponents or TextFields

XListBinding

Bind a list to a data model value/node. The binding allows a list model node to linked to a UI component so that it can be refreshed when new data is written to the model or conversely when the UI component needs to write data to the model.

This binding is designed to be used by list like components such as comboboxes or drop down lists.

XStateBinding

Bind a component's state to a data model value/node. The binding allows a model node to linked to a UI component so that it can be refreshed when new data is written to the model or conversely when the UI component needs to write data to the model.

This binding is designed to be used by components such as Checkboxes.

This state change does not affect the content displayed by the component

Happily once the bindings have been constructed there is no difference between bindings constructed via Java or via XML.

Carousel contains other binding types some of which support features such as localization of lists, conversion of physical dimensions and filtering of data. Please refer to the API documentation for further details.

Update on page display

Carousel tries to ensure that the data visible on screen is always consistent with what is stored in the model. One of the most important times for this is during page transition. When a page is shown, its data must be updated and equally if any page had been visible its data must also be saved.

When switching pages Carousel goes through a number of steps to ensure that the displayed data is consistent with the model state. The steps are as follows:

1. The current page's data is saved by calling saveBoundComponentValues ().

2. The current page is marked as deactivated and pageDeactivated () is called.

3. The new page is added to the target container.

4. The new page's bindings are updated by calling updateBindings . This method will evaluate each binding's source and output attributes and invoke any callback methods. This gives the page the opportunity to modify its bindings prior to display.

5. The new page's values are updated by invoking
the updateBoundComponentValues method

6. The new page is marked as activated and pageActivated () is called.

You can interact with the update by implementing the pageActivated() and pageDeactivated() methods, or you can invoke the other methods to update the model at any point in the life of the application. In most cases however the data bindings take care of all the work needed to ensure the displayed data is up to date.

Saving values

As mentioned above a page's data is saved upon page transitions but this is not always sufficient and it may be necessary to explicitly save the data at some point. This can be accomplished by calling the page's saveBoundComponentValues () method. This method iterates all the data bindings on a page and updates the associated model nodes.

Updating values

Just as you may want explicit control of saving you may want to force updates to a page's data. Updates can be forced via the updateBoundComponentValues () method. This method can be invoked at just about any time and you may need to do so anytime you have a calculation of a piece of business logic that writes (or loads) data to the model.

Source and output nodes

Each data binding has a data source and optionally an output node. The role of the source node is to provide the data to be displayed by the bound component whereas the output node provides a place to save the user value or selection state.

In some cases it would not be desirable to save a user value to the same node as the input as this would either destroy the original value or modify the input dataset.

By default the output node defaults to the source node such that its path is ' xui_state/ '. Furthermore all output nodes are appended to the ' xui_state ' node.

The distinction between source and output nodes is a little gray. In some cases like a read only list it would not make sense to write selection information back to the list source so clearly in this the source and output should be maintained separate. In the case of an edit field things may not be so clear as the selection state (the caret position) is rarely of interest and one would therefore expect the input and output nodes to be the same. Carousel can handle both these situations but ultimately you remain in control and can override the default behavior by explicitly naming the nodes.

Callbacks

Consider for a moment the case of reusable forms, say for example the case of a contact details form contain names, addresses and phone numbers. Such a form is pretty simple and from the above documentation you should be able to setup bindings for such a form without too much difficulty.

However, things start to get a little more interesting if the form is reused (as is often the case for something as ubiquitous as an address form). If say in the case of a financial application you had joint application for a mortgage then each person would have to fill out and address form. Now we could accomplish this by duplicating the form or by showing the form, capturing the data and then moving it to the right place but it would be a lot of work for little gain. In Carousel there is another approach.

Carousel supports dynamic bindings that can be updated during the life of an application. At the heart of a dynamic binding is the callback. The syntax for the callbacks is:

Code Sample 14-8 - An embedded callback method

source="${myMethod(args)}"

where myMethod is the name of a public method in the page's class. The method can be argumentless or it can have String or integer arguments. The path can also contain multiple expressions and fixed elements, for example

Code Sample 14-9 - A callback substituting a path element

source="users/${getCurrentUser()}/firstName"

where getUserName evaluates to some sort of user ID that exists (or will exist) in the model. The callback syntax is loosely based on expression language syntax and is explained more fully in "Evaluated attributes and helpers" on page 223.

Dynamic bindings

Using the above technique it is possible to do things like switching users when showing user details on a page. All that needs to be done is have the getCurrentUser return a different ID and invoke the updateBindings method (which is invoked implicitly during page transitions anyhow).

Thus, as you process one applicant for the aforementioned joint mortgage application you can set the appropriate customer/user ID and then as the application process progresses you can begin the data capture process for the second user by updating the ID and redisplaying the form. Since the evaluated path has changed by the time the form is redisplayed the components on the form will be bound to different locations.

Adapters

Sometimes it is not possible to have a direct correlation between the model's data structure and a binding's use of that data. In such cases an intermediate adaptor is used. Normally the data factory takes care of the instantiation of adapters, but in some circumstances, for example when coding a specific feature in Java, it may be necessary to construct an adaptor.

The role of the adaptor is to allow the specification of bindings so that only the target component and the data source need be specified, i.e. the endpoints. The binding factory can then take care of all the rest.

Adapters are frequently used where a complex model node such as a table or list node are being mapped to a simple output type like an edit field. In most cases you need not be aware of the adapter's role in a binding but in some circumstances you may want to modify some of the adapter's properties. To do this you must use Java as the XML interface does not support such properties.

Binding tables

Using a databse table in a UI component such as a JTable is straightforward, here are some examples:

Code Sample 14-10 - Some sample table bindings

For now the output values can be ignored, they are used to save the state data of the bindings (the index of the selected record).

Selecting tables

For more complex queries it is possible to dynamically query a database, but to do this you need to use Java code and setup both the model node and the binding yourself.

A new database table can be configured as follows:

Code Sample 14-11 - Select DISTINCT values from a table

DatabaseTableModel completeVoltageTable = DatabaseTableModel.getTable( "Voltages" );

completeVoltageTable.setDistinct( true );

completeVoltageTable.setOrderField( "ID" );

completeVoltageTable.retrieve();

Here the code starts by retrieving the basic ' Voltages ' table and then sets some additional attributes to order the table by the ' ID ' field and sets it to retrieve only the distinct rows.

The data is not pulled from the database till the retrieve method is called. It is worth noting that of course once the node is in the model you do not need to repeat the process and can instead just bind components to the node as needed.

Sometimes it is not desirable to predefine a table and in such cases a completely new database model node can be prepared in Java code.

Code Sample 14-12 - Setup a database query

DatabaseTableModel voltageTable = new DatabaseTableModel();

// Set the query elements

// FROM clause, FIELDS, WHERE clause

voltageTable.setupTable( "CS_VOLTAGES",

"VOLTAGE_DESCRIPTION, VOLTAGE_MIN, VOLTAGE_MAX",

"FREQUENCY=50" );

voltageTable.retrieve();

The fields to retrieve and the where clause are specified with this approach.

As an alternative the complete SQL statement can also be specified using the setSqlStatement method.

Once the tables has been retrieved it can be used in a UI component by updating the component's bindings

Code Sample 14-13 - Set and refresh a table control

myTable.setModel( voltageTable );

updateBoundComponentValues();

Linking components

To build a form using multiple UI components bound to a database table we can use the output attribute of the bindings. The output attribute is used to save the state data of the bindings.

For components such as list bindings these attributes include the selected item in the list. When displaying a list the binding will try to have the list display the last selected item (the value pointed to by the output node).

Tables and other components that use the database bindings similarly save their state to the output node. The table binding in particular saves (by default) the current row index of the table. Therefore by changing the selected row on a bound table the selected row on the underlying database table is updated. Then if the UI components bind to the same output path they should all refer to the same table row.

An example of this is the page described below:

Code Sample 14-14 - A complete example of table usage

constraint="north" style="Heading"/>

headingStyle="TableHeading" style="TableData"

selectionStyle="TableSelection" horizontal_scrollbar="as needed"/>

headingStyle="TableHeading"

style="TableData" selectionStyle="TableSelection"

horizontal_scrollbar="as needed" updateModel="true"/>

headingStyle="TableHeading" style="TableData"

selectionStyle="TableSelection"

horizontal_scrollbar="as needed" updateModel="true"/>

type="ListSelection"/>

In the above example three tabs are shown, on the third a table an edit field and a drop down list are all bound to the same table node and each outputs to the same path. Changing the selection on the table causes the selection on the edit field and combo box to be updated.

This update occurs because the table handles the ' ListSelection ' event and in doing so causes the ' updateBoundComponentValues ' method to be called. This method updates all the UI components bound to the model.

The output attribute specifies the path within the model to which the selection attributes are saved. These attributes include the row selection index of a table control. Normally the output of a Carousel model is saved to a specific subpath in the model, the ' xui_state ' node and whenever an output path is specified it is automatically appended to this node.

In some cases it is desirable to refer to another source for this selection state. By specifying the absolute path within the model it is possible to address such paths rather than just the children of the ' xui_state ' node. In the case of a master-child setup it would be possible to link table selections using such a technique, the child table's output would be set to the master table's source path in such a scenario, e.g.

Code Sample 14-15 - Example linking of bindings

Finally, Carousel as a Java based system is by default case sensitive. This case sensitivity also applies to database look-ups. SQL in contrast can be configured to be case in-sensitive. To help support this we allow look-up of fields in both case sensitive and case in-sensitive modes. The case sensitivity is set with a startup parameter in the startup.properties file

Code Sample 14-16 - Flag case sensitivity

CaseSensitiveDatabase=false

Advanced attribute evaluation and libraries

One of the goals XUI is to help promote an MVC architecture. The Data Binding and Event Binding helps make this clean separation by putting the UI declaration in XML, separate from the business logic which is implemented in Java. One limitation of this mechanism is that the custom logic had to be routed through event handlers in classes derived from the XPage component.

This dependency on a UI component was undesirable in some cases and made it a little more difficult to implement libraries of reusable functions than we would have liked. So, as of version 2.0 we have extended the attribute and event bindings to solve this problem.

Evaluated attributes

Attributes within a XUI page can be specified dynamically, for example

Table 14-2 - Basic attribute evaluation

The code ${getContent()} is an expression that is evaluated at runtime each time the expression is encountered. For a page component declaration the expression is evaluated when the page is loaded but expressions can be used in other locations such as within the data model, the data bindings, the validations or the event bindings.

An evaluated attribute's implementing method is by default in the owner page such that a reference like ${myMethod()}, which would evaluate to a method in the current page with a signature like:

Table 14-3 - Method signature

public void myMethod();

In XUI 2.0 The attributes can also be defined in classes other than the current page or classes derived from XPage. The syntax for such expressions is as follows:

Table 14-4 - Extended attribute declarations

Syntax

Behavior

${mypackage.MyClass.myMethod(args...)}

to invoke a static method

${mypackage.MyClass[].myMethod(args...)}

to create a new instance of the class on each evaluation

${mypackage.MyClass[referenceName].myMethod(args...)}

for a named object instance

${myMethod[referenceName](args...)}

for a method contained within the invoking page

${[referenceName].myMethod(args...)}

for a method contained within the class instance referred to by the reference name.

where mypackage is the name of the Java package containing the class MyClass . The value of referenceName is a user defined value that identifies the instance of the class. The application instantiates an instance of the class when the expression is first encountered and thereafter maintains the instance with each subsequent call retrieving the same instance of the class. As in early versions, the method call can also contain zero or more arguments.

What this means in practice is that the class or classes implementing an applications business logic no longer need be derived from XPage. In this way it is possible to build libraries of reusable functions. Lets look at an example:

Table 14-5 - Sample extended attributes

1

2

3

4

5

In the above example we are recreating the simple calculator that we have used in other examples. While the example is a little contrived (we will document some more realistic examples later) it shows the new syntax in action.

Line 5: Shows something similar to the pre XUI 2.0 syntax (which is still valid) except that the handler method is now in a separate class ( com.xoetrope.library.Calculator ) and in a separate package to the page ( com.mypackage.ui.swing.SimpleCalculator ) class. The method being invoked is a static member of the class.

Line 6: Shows a similar call except that instead of a call to a static method a concrete instance of the class is constructed. Once the instance of the class is constructed it is labelled for subsequent use.

Line 7: Reuses the reference setup it line 6 and invokes a different method on the same object. The reference is project wide so the object associated with the name can also be used across pages.

Expression evaluators

The expressions are evaluated by an ExpressionEvaluator and each page has by default its own instance of the default expression evaluator. (The default evaluator delegates storage of the referenced classes to the project). However, the page allows this evaluator to be replaced and a different evaluator can be inserted. This replaceable evaluator allows a route to include other expression evaluators such as interpreters. As an example an evaluator for the Groovy language has been created.

Using evaluated attributes

OK, so we have seen how the attributes of an XML file can in fact be callbacks to methods in your page class or in some other class. What does this mean for the application?

Essentially this means that the model is dynamic, it can be adapted to meet the changing needs of your application as a session progresses. You are not restricted to the setup encoded in the XML at start-up.

The dynamic model also means the data structure specified in the XML (or in your code for that matter) can be mapped from one instance to another via the evaluated attributes and callbacks. We have already seen how this could be use with something as simple as an address form.

Evaluated attributes also make it possible to apply more advanced techniques like filtering data and providing access control. Once you get to grips with the basic functionality you should find the use of evaluated attributes a very powerful mechanism.

comments


If you were logged in you could rate this article or add a comment.