Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
{scrollbar}

h1. Data Binding and Model-View-Controller Framework

As the XUL framework progressed, Pentaho developed a need for a clean, underlying model-view-controller (MVC) architecture application. MVC provides for a clean separation between application data (models), view logic (controllers) and the user interface (views). This separation creates loosely coupled components that are easier to maintain and test. For a more in-depth look at building Pentaho XUL applications with the MVC pattern, please refer to the excellent MVC in XUL applications article by Aaron Phillips: [http://wiki.pentaho.com/display/PLATFORM/MVC+in+Pentaho+Xul+Applications]

At the heart of any MVC application framework lies a method for synchronizing data between the models and views. Many frameworks leave this "housekeeping" to the developer. XUL bindings offer a more developer-friendly approach. 

h2. What's in a Binding?

A XUL binding object consists of four primary and two optional pieces of data:

* Two *XulEventSource* objects -- one "Source," the other "Target" -- and a property for each to "Bind" together.
* A binding strategy (one-way, bi-directional, or bind-once).

A converter object to manage data translation between the two is also an option.

h2. Creating a Binding Object

There are several methods for creating a binding object in the XUL framework. Some are as simple as calling *bind()* with four strings from an event handler. All of those methods are covered in the aforementioned MVC document. Below is the most basic representation of a binding:

{code}
Binding bind = new Binding(sourceObject, "firstName", targetObject, "value");
{code}

When added to a binding context (explained later in this guide) it will synchronize data between *sourceObject* and *targetObject* by way of *firstName* and *value* respectively.

Because Java lacks first-class support for properties, and it's bad form to write objects with direct member access, a binding accesses data by way of the JavaBean standard. At the JavaBean level, the binding looks something like this:

{code}
sourceObject.getFirstName() => targetObject.setValue()
sourceObject.setFirstName() <= targetObject.getValue()
{code}

h2. Binding Strategies

By default, a binding object represents a synchronization bi-directionally. Any change to the source will update the target and visa-versa. You can optionally prescribe for a one-way binding between source and target as such:

{code}
binding.setBindingType(Binding.Type.ONE_WAY);
{code}

Where *ONE_WAY* is a type defined in Binding.Type enumeration. A one-way binding will send data from the source to the target but not the other way around.

Future versions will introduce the concept of a "bind-once" strategy that will in essence "flash" a snapshot of data between objects at the time of the binding's instantiation.

h2. Conversions

You can further extend the flexibility of your bindings by providing a *BindingConvertor* object to manage the translation of data between objects:

*BindingConvertor.java*
{code}
public abstract class BindingConvertor<V, R> {
    ...
    public abstract R sourceToTarget(V value);
    public abstract V targetToSource(R value);
}
{code}

Below is a simple implementation that binds the value of a *XulTextbox* with the *selectedIndex* of a *XulMenuList*. When a user enters a numeric string into the text box, it will be converted into an integer before being passed to the *setSelectedIndex* method of the menu list:

{code}
Binding binding = new Binding(textbox, "value", dropdown, "selectedIndex");

BindingConvertor conversion = new BindingConvertor<String, Integer>(){
    @Override
    public Integer sourceToTarget(String value) {
        return Integer.parseInt(value);
    }

    @Override
    public String targetToSource(Integer value) {
        return value.toString();
    }
}

binding.setConversion(conversion);
{code}

h2. The Binding Context

Bindings in and of themselves don't actually do anything; they simply describe the relationship between objects. The actual establishment of a binding is performed by a *BindingContext* object. However, you never have to deal with a *BindingContext* directly because every *XulDomContainer* has one. To add a new binding object to the context, simply pass the binding to the *addBinding()* method of the *XulDomContainer*.

{scrollbar}