Click here to Skip to main content
Click here to Skip to main content

Flex Data Binding Tricks

, , 2 Feb 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
This article describes the usage of Data Binding in Flex applications

Introduction

Some time ago, working on a Flex client application project, I've discovered the Flex Data Binding (I think anyone, who starts working with Flex meets with Data Binding very soon). Now, when I have some knowledge in that area, I want to share a few tips to make it easier for the beginner to utilize data binding in Flex applications.

By using Data Binding, we can easily link objects (data sources) between each other, which gives us a possibility to keep them synchronized. One of the areas, where data binding may be used is the linking of user interface elements between each other with the aim to provide a more interactive UI.

Simple Data Binding with MXML

Let us assume that we have two text fields:

<mx:TextInput id="sourceField" text="" />
<mx:TextInput id="destinationField" text="" />

We want the changes performed in the first input to be displayed on a second input. To achieve this, the following mxml code could be used:

<mx:Binding destination="destinationField.text" source="sourceField.text"/>

As a result the text, which is typed in a first text input will be automatically placed in a second text input. And this is what simple data binding stands for. The code of a simple application, which uses data binding, is as follows:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
   <mx:Binding destination="destinationField.text" source="sourceField.text"/>
   <mx:VBox>
      <mx:TextInput id="sourceField" text="" />
      <mx:TextInput id="destinationField" text="" />
   </mx:VBox>
</mx:Application>

Simple Data Binding with ActionScript

The above example is easy to transform to ActionScript. The benefit we get here is that we can use the data binding for dynamically created elements. We have the same two text fields:

public var sourceField : TextInput = new TextInput(); 
public var destinationField : TextInput = new TextInput();

Data binding will look like:

BindingUtils.bindProperty(destinationField, "text", sourceField, "text");

The first bindProperty function parameter is a destination object, the second parameter is a destination object property name, the third parameter is a departure object and the fourth is a chain-object, which in a simple case represents a departure object property name (we will look in more detail on this parameter later on). And here goes an application code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import mx.controls.TextInput;
         import mx.binding.utils.BindingUtils;
      
         public var sourceField : TextInput = new TextInput();
         public var destinationField : TextInput = new TextInput();
         public function init():void
         {
            sourceField.text = "";
            parentContainer.addChild(sourceField);
            
            destinationField.text = "";
            parentContainer.addChild(destinationField);
            
            BindingUtils.bindProperty(destinationField, "text", sourceField, "text");
         }               
      ]]>
   </mx:Script>
   <mx:VBox id="parentContainer"/>
</mx:Application>

Chain Object in a bindProperty Method

This object is used as a fourth parameter of a BindingUtils.bindProperty function. It describes, in which way and to which parameter the data binding should be applied in a departure object. This object may be represented in three different forms: String, array and compound object.

String representation, which we used already, contains the property name of the departure object.

Array representation holds access hierarchy to inner property of a departure object. To understand its usage, let us create an example where two objects are used:

package src
{
   public class SimpleObject
   {
      public var myText:String = new String();
      public function SimpleObject():void
      {
         myText = "empty";
      }
   }
}
 
package src
{
   public class ComplexObject
   {
      public var simpleInstance: SimpleObject = new SimpleObject();
      public function ComplexObject():void
      {
         //some logic
      }
   }
}

...

public var destinationField : TextInput = new TextInput();
public var complexInstance : ComplexObject = new ComplexObject();

So we have a ComplexObject class instance and we want to connect the myText field (which is inside the inner SimpleObject object class) with the text property of a TextInput object. That is we want to bind complexInstance.simpleInstance.myText to destinationField.text. We will use an array as a chain object to achieve that:

 BindingUtils.bindProperty(
		destinationField, 
		"text", 
		complexInstance, 
		["simpleInstance","myText"]);

Flex will then concatenate the array data with points and will bind to property complexInstance.simpleInstance.myText. An example application with the array type chain usage will look as follows:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import src.ComplexObject;
         import mx.controls.TextInput;
         import mx.binding.utils.BindingUtils;
      
         public var destinationField : TextInput = new TextInput();
         public var complexInstance :  ComplexObject = new ComplexObject();
         public function init():void
         {
            destinationField.text = "";
            parentContainer.addChild(destinationField);
            
            BindingUtils.bindProperty(destinationField, 
				"text",
				complexInstance, 
				["simpleInstance","myText"]);
         }
      ]]>
   </mx:Script>
   <mx:VBox id="parentContainer"/>
</mx:Application>

In a compound object case, the chain is represented as:

var object : Object = {name:<property name>, getter: <value receiver function>};

where property name is a string with a departure object property name and value receiver function:

function (host:<object of a departure type >) : <type of a return value>
{
	// function body, where we can query the host parameter
	// since we know this is an object, used for departure
}

Assume we have an instance of ArrayCollection class and a text field.

public var array : ArrayCollection = new ArrayCollection();
public var destinationField : TextInput = new TextInput(); 

We want to disable the text field when the number of objects inside the collection will exceed the quantity of 10 and enable it otherwise.

BindingUtils.bindProperty(
		destinationField, 
		"enabled", 
		array, 
		{
			name:"length",
			getter : function (host : ArrayCollection):Boolean 
						{ return host.length<10; }
		});

And here is a full application code with compound object as a chain usage:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import mx.collections.ArrayCollection;
         import mx.controls.TextInput;
         import mx.binding.utils.BindingUtils;
      
         public var destinationField : TextInput = new TextInput();
         [Bindable]
         public var array : ArrayCollection = new ArrayCollection();
         public function init():void
         {
            destinationField.text = "some text";
            parentContainer.addChild(destinationField);
            
            BindingUtils.bindProperty(
				destinationField, 
				"enabled", 
				array, 
				{
					name:"length",
					getter : function (host : 
						ArrayCollection):Boolean 
						{ return host.length>=10; }
				});
         }
      ]]>
   </mx:Script>
   <mx:VBox id="parentContainer">
      <mx:Button label="add element" click="array.addItem(0);"/>
      <mx:Text text="array length: {array.length}"/>
   </mx:VBox>
</mx:Application>

bindSetter Method from BindingUtils Class

Another approach of a data binding usage is to invoke a callback function, when property of a departure object is changed. Assume we have a collection and a text field.

public var destinationField : TextInput = new TextInput();
public var array : ArrayCollection = new ArrayCollection();

Collection contains digits, which are added to the collection from outside. We need to display the sum of all collection elements in a text field (and we need to keep this sum up to date). This means we need to recalculate the sum of the elements, when the length of a collection changes. To achieve that, we will use the bindSetter method of a BindingUtils class:

BindingUtils.bindSetter(calculateSum , array, "length");

where calculateSum will be a function:

function ( input : <departure>):void
{
	//function contents
}

In our case, calculateSum will contain the following:

public function calculateSum(input : Number):void
{
	var sum:Number = 0;
	for (var i:int = 0; i<input; i++)
	{
		sum += array[i];
	}
	destinationField.text =  sum.toString();
}

And here is an application code for the callback function usage:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import mx.collections.ArrayCollection;
         import mx.controls.TextInput;
         import mx.binding.utils.BindingUtils;
      
         public var destinationField : TextInput = new TextInput();
         [Bindable]
         public var array : ArrayCollection = new ArrayCollection();
         public function init():void
         {
            destinationField.text = "some text";
            parentContainer.addChild(destinationField);
            
            BindingUtils.bindSetter(calculateSum , array, "length");
         }
         
         public function calculateSum(input : Number):void
         {
            var sum:Number = 0;
            for (var i:int = 0; i<input; i++)
            {
               sum += array[i];
            }
            destinationField.text =  sum.toString();
         }
 
      ]]>
   </mx:Script>
   <mx:VBox id="parentContainer">
      <mx:Button label="add element" click="array.addItem(Math.random());"/>
      <mx:List width="100" height="200" dataProvider="{array}"/>
   </mx:VBox>
</mx:Application>

Using bindSetter with a Compound Chain Object

This is the most complex type of a data binding, but it is easy to understand. Assume we need to write a class, which collects data about the width changes, which happen with the observed button component. First, we write the class itself:

package src
{
   import mx.collections.ArrayCollection;  
   public class Collector
   {
      public var array : ArrayCollection;      
      public function Collector():void
      {
         array = new ArrayCollection();
      }      
      public function collect(str:String):void
      {
         array.addItem(str);
      }
   }
}

and a return function, which prepares data about the button:

public function returnInfo(host:UIComponent):String
		{
           return "width:" + host.width + "; height:" + host.height;
        }

Now, using bindSetter method we will define that when a width property of a button component changes, the returnInfo function should be invoked:

BindingUtils.bindSetter(collector.collect, button, 
		{name:"width", getter: returnInfo});

And here is an application which demonstrates the compound chain object usage:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import mx.core.UIComponent;
         import mx.binding.utils.BindingUtils;
         import src.Collector;
         public var collector : Collector = new Collector();
         
         public function init():void
         {
            BindingUtils.bindSetter(collector.collect, button, 
            		{name:"width", getter: returnInfo});
            data.dataProvider = collector.array;
         }
         
         public function returnInfo(host:UIComponent):String
         {
            return "width:" + host.width + 
            	"; height:" + host.height;
         }
 
         public function changeButton():void
         {
            button.width = Math.round(Math.random() * 200);
            button.height = Math.round(Math.random() * 100);   
         }
      ]]>
   </mx:Script>
   <mx:VBox>
   <mx:Button label="change" click="changeButton();"/>
   <mx:List id="data" width="200" height="400" />
   <mx:Button id="button" width="200" height="100" label="mybutton"/>
   </mx:VBox>
</mx:Application>

The returnInfo function prepares data about a button width, then this data automatically goes to function collector.collect() where it is then handled appropriately.

History

  • 2nd February, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Authors

Olikhver Sergey
Web Developer IntexSoft
Belarus Belarus
No Biography provided

IntexSoft
IntexSoft
Belarus Belarus
IntexSoft (Intellectual Export Software) is dynamic software development company offering high quality cost-effective IT services to customers worldwide. We develop custom software across a range of industry, such as Media Production, Product Information Management, Translation Management.
 
The company possesses high expertise in J2EE, Adobe Flex/AIR and also in software integration with products like Adobe Indesign Server, Quark Xpress Server, Across Server and others.
 
Founded in 1999, our development centre is located in Grodno, Belarus - historically the most technically advanced region in the former USSR.
Group type: Organisation

4 members


Comments and Discussions

 
GeneralAcai Max Cleanse Pinmemberjoyvirgin25-Jul-10 23:53 
GeneralDogpedic Pinmembernoisebim bim11-Feb-10 18:14 
GeneralTri Cleanse Pinmemberjaisesine6-Feb-10 16:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.141022.1 | Last Updated 2 Feb 2010
Article Copyright 2010 by Olikhver Sergey, IntexSoft
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid