Less than a year ago, I was involved in a project for which it was necessary to write a client in Flex. Since I was new to this business, I found something new and completely unknown to me in the process. At that time, I discovered the Flex Data Binding (data binding). I think everyone who is working with Flex will very soon encounter Data Binding.
Data binding is that we can easily connect two objects (data source) to each other, which allows us to keep them in sync. An example is the interconnection of user interface elements by creating some rules of behavior that help create a more interactive user interface.
Having some experience in this area, I decided to deconstruct all types of data binding mechanism in Flex. I think this article will be interesting not only for beginners, but also professionals will get something from it for themselves.
Simple data binding using MXML
All data binding documents (which I read at least) begin with this type of use of this remarkable function. Unfortunately, this is practically the only method that is clearly described everywhere. So, suppose we have two text fields:
<mx:TextInput id="sourceField" text="" />
<mx:TextInput id="destinationField" text="" />
We want the changes in the first field to appear in another field. For this situation, it is enough to write the following mxml code:
<mx:Binding destination="destinationField.text" source="sourceField.text"/>
This will lead to the fact that the text entered in the first text field will automatically be installed in the second text field. This is the meaning of simple data binding. Simple application code using data binding:
')
<?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 using ActionScript
The example described above is easy to rewrite in ActionScript. The meaning of this design is that we can use it for dynamically created elements. So, all the same two text fields:
public var sourceField : TextInput = new TextInput();
public var destinationField : TextInput = new TextInput();
Data binding for these fields will look like this:
BindingUtils.bindProperty(destinationField, "text", sourceField, "text");
The first parameter of the bindProperty function is the “destination” object, the second parameter is the string with the property name of this object, the third parameter is the “departure point” object and the fourth parameter is a chain object, which, when simplified, can be the string with the property name of the “departure point "(This parameter will be discussed in more detail below). Application code using this data binding method:
<?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>
The chain object in the bindProperty method of the BindingUtils class.
Consider in more detail the object chain, which was mentioned earlier. This object is used as the 4th parameter of the bindProperty function in the BindingUtils class. This object describes how and to which parameter to apply the data binding in the "point of departure" object. This object can be presented in three different forms:
Line
Line - stores in itself the name of the object property "point of departure". That is, having a text field with the name “sourceField” we can use the name of the option “text” as the chain object. This kind of chain object has been discussed above. Additional clarifications will be superfluous.
Array of strings
Array of lines - stores in the hierarchy of access to the internal property of the object “point of departure”. The purpose of this type of chain object is very easy to understand by analyzing a simple example that uses only two objects:
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 {
Suppose we have an instance of the class ComplexObject, with which we need to associate a text field, namely the property myText (which is in the internal object of the class ComplexObject) must be associated with the text property of the text field. Initialization code:
public var destinationField : TextInput = new TextInput();
public var complexInstance : ComplexObject = new ComplexObject();
As mentioned above, we need to associate complexInstance.simpleInstance.myText with destinationField.text. With data binding, this is done like this:
BindingUtils.bindProperty( destinationField, "text", complexInstance, ["simpleInstance","myText"]);
In this example, the array hierarchy is used as the chain object. In the future, all the elements of the array are combined through a point in one line, which is used as a property of the “departure point” object. Full application code using this data binding method:
<?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>
Composite object
Composite object - in this case the chain object is an object of the type:
var object : Object = {name:< >, getter: < >};
property name - there is a string that contains the property name of the "dispatch object"
function of getting the value - this function looks like:
function (host:< « » >) : < > {
To understand this type of chain object, you can use a very simple data binding example. For example, we have an instance of the ArrayCollection class and a text field. The condition of our task will be to disable the text field when the number of objects contained in our collection of 10 pieces is reached. Declaring the required variables:
public var array : ArrayCollection = new ArrayCollection();
public var destinationField : TextInput = new TextInput();
Using data binding in this case:
BindingUtils.bindProperty( destinationField, "enabled", array, { name:"length", getter : function (host : ArrayCollection):Boolean { return host.length<10; } });
As we can see, the code is simple and straightforward. In this case, the data binding will work as we need: turn off the text field when the number of elements in the collection is greater than or equal to 10 and accordingly turn on when the number of elements changes to the lower side. Full application code using this data binding method:
<?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(); 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>
The bindSetter method of the BindingUtils class.
Consider another way of data binding. This method consists in the fact that we can call the similarity of the call back function when the properties of the “departure point” object change.
Consider a simple example. Suppose we have a collection and a text field. The conditions of the problem are as follows: the collection stores numerical values, these values ​​are added to the collection from the outside. We need to display the sum of all elements of the collection in the text field (and the amount should always be relevant). Those. in terms of data binding, we must, when changing the length of the collection, recalculate the sum of all its elements. In this case, the bindSetter method of the BindingUtils class is used.
First, initialize the variables:
public var destinationField : TextInput = new TextInput();
public var array : ArrayCollection = new ArrayCollection();
And now we describe the data binding operation:
BindingUtils.bindSetter(calculateSum , array, "length"); calculateSum function ( input : < « »>):void {
In our case, the implementation of the function calculateSum will be as follows:
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(); }
Those. all as we expected. When changing the length of the collection, the amount is recalculated
its elements and displayed in the text box.
Full application code using this data binding method:
<?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(); 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 the bindSetter method with a composite view of a chain object
This is the most difficult type of data binding, but this does not mean that it is very difficult to understand. Traditionally back to the formulation of the problem.
It is necessary to write a collector class that collects data on changes that have occurred with the observed component. The class will store an array of information objects (with the width, height, and position of the observed component).
Let's write to start a class:
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); } } }
The application will look like this:
<?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>
Pay attention to the function init () namely the string
BindingUtils.bindSetter(collector.collect, button, {name:"width", getter: returnInfo});
It carries the information that when the “width” property of the button component is changed, we will call the returnInfo function, which will generate data about the button, this data will be automatically transferred to the collector.collect () function in which they will be processed accordingly.
That's all that I wanted to tell.
PS The topic was specifically written for the blog Adobe Flex.
I think he will get there.