Data Binding

Flex applications typically utilize lots of data retrieved from both RPCs (server-side method calls) and user input collected in forms. One of the ways in which you can work with data is to use extensive ActionScript. ActionScript provides you with low-level access to all the data in your Flex application. Yet the ActionScript code can be redundant, and it can be time-consuming to write. Although extensive ActionScript may be necessary in some cases, the Flex framework provides a feature called data binding that simplifies working with data in most cases.

Data binding lets you associate data from one object with data from another object. There are lots of ways to use data binding. The following examples list a few of the most common uses for data binding:

  • Link form input controls (text inputs, checkboxes, etc.) with data models.

  • Link two or more controls (e.g., display a slider value in a text component).

In the following sections, we’ll look at the rules of data binding as well as examples of different ways to use data binding.

Understanding Data Binding Syntax

There are three basic ways to apply data binding:

  • Curly brace ({}) syntax

  • <mx:Binding>

  • BindingUtils

Each of these techniques for applying data binding has advantages and disadvantages, which we’ll discuss in the next few sections.

Curly braces

Using curly braces to apply data binding is the simplest and fastest technique. Throughout the early part of this book, you’ve seen quite a few examples of curly brace syntax. Placing curly braces around any expression causes it to be evaluated. Consider the following example with a combo box and a text input control:

<mx:HBox>
    <mx:ComboBox id="level">
        <mx:Array>
            <mx:Object label="A" data="1" />
            <mx:Object label="B" data="2" />
            <mx:Object label="C" data="3" />
            <mx:Object label="D" data="4" />
        </mx:Array>
    </mx:ComboBox>

    <mx:TextInput id="selectedLevel" text="level.value" />
</mx:HBox>

In this example, the text attribute of the text input is set to level.value. In that format, the value is interpreted literally, so the string level.value displays in the text input. Changing the text input tag to the following makes a big difference:

<mx:TextInput id="selectedLevel" text="{level.value}" />

With this change the text input now selects the data corresponding to the selected combo box item. As the user selects a different combo box item, the value in the text input also updates. This is because the text level.value is now placed within curly braces, so it is treated as an expression rather than as literal text.

More than just evaluating the expression, the curly braces attempt to make a data binding association. If the association is successful, as the value of the target (in the example, the target is level.value) changes, the listening property (the text property of the text input in this example) also updates. The preceding example illustrates this because as the combo box value changes, so does the value displayed in the text input. For a more dramatic example, consider the following:

<mx:Panel id="panel" width="{panelWidth.value}" height="{panelHeight.value}">
    <mx:NumericStepper id="panelWidth"  value="200" minimum="200"
                       maximum="400" stepSize="10" height="22"/>
    <mx:NumericStepper id="panelHeight"  value="200" minimum="200"
                       maximum="400" stepSize="10" height="22"/>
</mx:Panel>

In this example, the panel contains two nested numeric steppers. The panel uses data binding to link the width property to the value of the first numeric stepper and the height property to the value of the second stepper. So as the user changes the values of the numeric steppers, the width and height of the panel change accordingly.

There are many scenarios in which you can use curly brace syntax for data binding. As you’ve seen in the preceding example, you can use the syntax to directly associate a target property with a property of a form control such as a text input. You can also link a value from a control to a data model, as the following example illustrates:

<mx:Model id="dataModel">
    <userData>
        <email>{email.text}</email>
        <phone>{phone.text}</phone>
        <city>{city.text}</city>
        <state>{state.value}</state>
    </userData>
</mx:Model>

<mx:VBox>
    <mx:Label text="Email" />
    <mx:TextInput id="email" />
    <mx:Label text="Phone" />
    <mx:TextInput id="phone" />
    <mx:Label text="City" />
    <mx:TextInput id="city" />
    <mx:Label text="State" />
    <mx:ComboBox id="state">
        <mx:Array>
            <mx:Object label="CA" />
            <mx:Object label="MA" />
        </mx:Array>
    </mx:ComboBox>
</mx:VBox>

The preceding code uses data binding to link the values from form controls to a data model. You can use data binding both to assign values to a data model, as in the preceding example, and to retrieve data from a data model and display it. And you can even use data binding in both directions at the same time. The following code, used in conjunction with the preceding example, formats and displays the text from the data model in a text area, updating as the user changes the values in the controls bound to the model.

<mx:TextArea width="200" height="200" text="{'Contact Information
Email: ' +
             dataModel.email + '
Phone: ' + dataModel.phone + '
Location: ' +
             dataModel.city + ', ' + dataModel.state}" />

Perhaps an even more useful example is one in which you use data binding to link data either directly from controls or from a data model to an RPC component such as a RemoteObject component. Using data binding in this way allows you to make RPCs without having to write much, if any, ActionScript. The following example uses data binding to link the data from the data model in the preceding example to a RemoteObject instance as the parameters for a method call:

<mx:RemoteObject id="example" destination="exampleService">
    <mx:method name="saveContactInformation">
        <mx:arguments>
            <email>{dataModel.email}</email>
            <phone>{dataModel.phone}</phone>
            <city>{dataModel.city}</city>
            <state>{dataModel.state}</state>
        </mx:arguments>
    </mx:method>
</mx:RemoteObject>

As the values in the data model update via data binding, so, too, will the values in the RemoteObject method arguments update. This allows you to call the method by simply calling a send() method with no parameters, as in the following example:

<mx:Button label="Save" click="example.saveContactInformation.send()" />

This is a very simple example of working with RemoteObject. The same principles are true when working with HTTPService and WebService. We discuss all of these RPC techniques in more detail in Chapter 17.

Because curly brace syntax allows you to evaluate any ActionScript expression, you can also use data binding with E4X expressions. That means you can use data binding not only to link XML data with control values, but also to link controls and RPC components to XML values using E4X expressions. For instance, instead of using a Model object, as in the earlier example, you can use an XML object as follows:

<mx:XML id="xmlData">
    <userData email="{email.text}" phone="{phone.text}"
              city="{city.text}" state="{state.value}" />
</mx:XML>

You can then use E4X expressions to link the text area value to values from the XML object:

<mx:TextArea width="200" height="200" text="{'Contact Information
Email: ' +
             xmlData.@email + '
Phone: ' + xmlData.@phone + '
Location: ' +
             xmlData.@city + ', ' + xmlData.@state}" />

<mx:Binding>

The <mx:Binding> tag allows you to do exactly the same things as curly brace syntax, but with MXML tags rather than inline expressions. The <mx:Binding> tag requires the following attributes:

source

The origin of the data you want to link

destination

The point which you want notified when the value changes from the source

For the following example, the same basic premise is used as in the first example of the curly brace discussion: we’ll link the value from a combo box to a text input so that when the user changes the value in the combo box, the value in the text input also changes. First we’ll add the controls:

<mx:HBox>
    <mx:ComboBox id="level">
        <mx:Array>
            <mx:Object label="A" data="1" />
            <mx:Object label="B" data="2" />
            <mx:Object label="C" data="3" />
            <mx:Object label="D" data="4" />
        </mx:Array>
    </mx:ComboBox>

    <mx:TextInput id="selectedLevel" />
</mx:HBox>

Notice that we’re not setting the text property of the text input. With curly brace syntax we’d set the text property inline. With the <mx:Binding> tag we’ll use a separate tag to achieve the goal of data binding. To link the source (level.value) and the destination (selectedLevel.text), you can add the following tag to your code (note that the tag must appear outside any layout container tags):

<mx:Binding source="level.selectedItem.data" destination="selectedLevel.text" />

This code works identically to how the curly brace example worked, yet it uses a different mechanism to achieve that goal.

You can use <mx:Binding> with data models and RPC components as well. We can rewrite the earlier data model example to illustrate this point. First, add the data model, the RemoteObject, and the controls. Note that in this example, the data model and the remote method arguments do not define any values inline:

<mx:Model id="dataModel">
    <userData>
        <email></email>
        <phone></phone>
        <city></city>
        <state></state>
    </userData>
</mx:Model>

<mx:RemoteObject id="example" destination="exampleService">
    <mx:method name="saveContactInformation">
        <mx:arguments>
            <email></email>
            <phone></phone>
            <city></city>
            <state></state>
        </mx:arguments>
    </mx:method>
</mx:RemoteObject>

<mx:VBox>
    <mx:Label text="Email" />
    <mx:TextInput id="email" />
    <mx:Label text="Phone" />
    <mx:TextInput id="phone" />
    <mx:Label text="City" />
    <mx:TextInput id="city" />
    <mx:Label text="State" />
    <mx:ComboBox id="state">
        <mx:Array>
            <mx:Object label="CA" />
            <mx:Object label="MA" />
        </mx:Array>
    </mx:ComboBox>
    <mx:TextArea id="summary" width="200" height="200" />
</mx:VBox>

Next, we need to define the data bindings using the <mx:Binding> tag:

<mx:Binding source="email.text" destination="dataModel.email" />
<mx:Binding source="phone.text" destination="dataModel.phone" />
<mx:Binding source="city.text" destination="dataModel.city" />
<mx:Binding source="state.value" destination="dataModel.state" />
<mx:Binding source="'Contact Information
Email: ' + dataModel.email +
    '
Phone: ' + dataModel.phone + '
Location: ' + dataModel.city + ', ' +
    dataModel.state" destination="summary.text" />
<mx:Binding source="dataModel.email"
            destination="example.saveContactInformation.arguments.email" />
<mx:Binding source="dataModel.phone"
            destination="example.saveContactInformation.arguments.phone" />
<mx:Binding source="dataModel.city"
            destination="example.saveContactInformation.arguments.city" />
<mx:Binding source="dataModel.state"
            destination="example.saveContactInformation.arguments.state" />

You can also use E4X expressions with the <mx:Binding> tag. Assume you change the data model from a Model object to an XML object as follows:

<mx:XML id="xmlData">
    <userData email="{email.text}" phone="{phone.text}"
              city="{city.text}" state="{state.value}" />
</mx:XML>

You can then change the <mx:Binding> tags as follows:

<mx:Binding source="email.text" destination="xmlData.@email" />
<mx:Binding source="phone.text" destination="xmlData.@phone" />
<mx:Binding source="city.text" destination="xmlData.@city" />
<mx:Binding source="state.value" destination="xmlData.@state" />
<mx:Binding source="'Contact Information
Email: ' + xmlData.@email + '
Phone: ' +
            xmlData.@phone + '
Location: ' + xmlData.@city + ', ' +
            xmlData.@state" destination="summary.text" />
<mx:Binding source="xmlData.@email"
            destination="example.saveContactInformation.arguments.email" />
<mx:Binding source="xmlData.@phone"
            destination="example.saveContactInformation.arguments.phone" />
<mx:Binding source="xmlData.@city"
            destination="example.saveContactInformation.arguments.city" />
<mx:Binding source="xmlData.@state"
            destination="example.saveContactInformation.arguments.state" />

The <mx:Binding> tag requires more code than the curly brace syntax. Curly brace syntax can appear inline within existing tags, whereas <mx:Binding> syntax requires that you add additional tags to your code. This may seem like a disadvantage at first; however, in the long run it is advantageous to use <mx:Binding> in most cases because it allows you to create a cleaner separation between the UI layout and the data used. Using the <mx:Binding> tag is a cleaner implementation of data binding, yet it does not allow any greater functionality than curly brace syntax. If you need more functionality (such as dynamically changing data binding endpoints at runtime), you can use BindingUtils, discussed in the next section.

Note

Although using <mx:Binding> may have its advantages (as mentioned in the preceding paragraph), the disadvantage is that it requires more code to accomplish the same thing as simply using curly braces within component tags. This is a trade-off that you must evaluate for yourself.

BindingUtils

In most cases, you should use curly brace or <mx:Binding> syntax for data binding. However, neither of those techniques let you dynamically configure data binding at runtime. The mx.binding.utils.BindingUtils class has a static method called bindProperty() that lets you configure data binding from ActionScript. This ActionScript solution provides the most flexibility and the lowest-level access to data binding of all the techniques. As such, the BindingUtils.bindProperty() method can be a useful resource in those special cases in which you require more flexibility than the other techniques afford you.

The syntax for the bindProperty() method is as follows:

BindingUtils.bindProperty(destinationObject, destinationProperty,
                          sourceObject, sourceProperty);

The destination and source object parameters are object references and the property parameters are strings. The following example links the value from a combo box so that it displays in a text input:

BindingUtils.bindProperty(textInput, "text", comboBox, "value");

Because BindingUtils is ActionScript, you can place the code anywhere that you can place ActionScript code. The following example uses a button to enable data binding between a combo box and a text input when the user clicks the button:

<mx:Script>
    <![CDATA[
        import mx.binding.utils.BindingUtils;
    ]]>
</mx:Script>

<mx:VBox>
  <mx:ComboBox id="comboBox">
    <mx:Array>
      <mx:Object label="1" />
      <mx:Object label="2" />
      <mx:Object label="3" />
      <mx:Object label="4" />
    </mx:Array>
  </mx:ComboBox>
  <mx:TextInput id="textInput" />
  <mx:Button label="enable data binding"
   click="BindingUtils.bindProperty(textInput, 'text', comboBox, 'value')" />
</mx:VBox>

In the preceding example, the combo box and the text input are not initially linked. However, when the user clicks on the button, it calls the bindProperty() method, which links the controls such that the combo box value is displayed in the text input, and the display changes as the value changes. To use BindingUtils, you must add an import statement, as in the example.

The bindProperty() method returns a reference to a new mx.binding.utils.ChangeWatcher object. The ChangeWatcher class defines a class of objects that represents the actual data binding link between a source and a destination. Using bindProperty() by itself allows you to enable data binding at runtime, but if you want to further modify that data binding, you’ll have to work with a ChangeWatcher object. Using a ChangeWatcher object, you can disable data binding or change the source point.

The ChangeWatcher object returned by a bindProperty() method call represents that data binding association, and if you want to change that association or stop it, you must use the ChangeWatcher object. You can stop a data binding association between two points by calling the unwatch() method of the ChangeWatcher object:

changeWatcher.unwatch();

You can retrieve the current source value using the getValue() method:

changeWatcher.getValue();

You can change the source object using the reset() method. The reset() method accepts one parameter specifying the new source object:

changeWatcher.reset(newSourceObject);

The reset() method does not allow you to change the property of the source object. If you want to change the property, you must call unwatch() to stop the current data binding association. Then you can start a new association using BindingUtils.bindProperty():

changeWatcher.unwatch();
changeWatcher = BindingUtils.bindProperty(newSource, newProperty, destination,
destinationProperty);

Example 14-2 uses BindingUtils and ChangeWatcher to toggle the source object between two combo boxes. The example uses two combo boxes, a text input, and a button. When the application initializes, it calls the initializeHandler() method as that is assigned to the Application initialize handler. The initializeHandler() method sets data binding between the level combo box and the selectedLevel text input. When the user clicks the button, the data binding system calls the toggleDataBinding() method, which uses the reset() method of the ChangeWatcher object to change the source object.

Example 14-2. Working with BindingUtils

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
 initialize="initializeHandler(event)">

  <mx:Script>
    <![CDATA[

        import mx.binding.utils.BindingUtils;
        import mx.binding.utils.ChangeWatcher;

        private var _changeWatcher:ChangeWatcher;
        private var _currentHost:ComboBox;

        private function initializeHandler(event:Event):void {
            // Set the initial data binding, and assign the ChangeWatcher
            // object to the _changeWatcher property.
            _changeWatcher = BindingUtils.bindProperty(selectedLevel, "text",
                                                       level, "value");

            // Save a reference to the current source object.
            _currentHost = level;
        }

        private function toggleDataBinding(event:Event):void {

            // Determine the new source object. If the current source
            // object is level, set the new source to subLevel. If the
            // current source object is subLevel then set the new
            // source to level.
            _currentHost = _currentHost == level ? subLevel : level;

            // Use the reset() method to change the source object.
            _changeWatcher.reset(_currentHost);

            // Calling reset() changes the source for the data binding, but it does
            // not immediately update the destination. For that, you need to
            // manually update the destination value by retrieving the source value
            // using the getValue() method of the ChangeWatcher object.
            selectedLevel.text = _changeWatcher.getValue().toString();
        }

    ]]>
  </mx:Script>

  <mx:VBox>
    <mx:ComboBox id="level">
        <mx:Array>
            <mx:Object label="A" data="1" />
            <mx:Object label="B" data="2" />
            <mx:Object label="C" data="3" />
            <mx:Object label="D" data="4" />
        </mx:Array>
    </mx:ComboBox>

    <mx:ComboBox id="subLevel">
        <mx:Array>
            <mx:Object label="A" data="1.1" />
            <mx:Object label="B" data="1.2" />
            <mx:Object label="C" data="1.3" />
            <mx:Object label="D" data="1.4" />
        </mx:Array>
    </mx:ComboBox>

    <mx:TextInput id="selectedLevel" />

    <mx:Button label="toggle data binding" click="toggleDataBinding(event)" />

  </mx:VBox>
</mx:Application>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset