1. One-way binding & Two-way binding
1) One-way binding: Source ->Destination, once source changed, destination will change accordingly.
2) Two-way binding: Source<->Destination, once source changed, destination will change and vice versa.
Eg.
1)One-way binding
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"> <fx:Declarations> <!-- Place non-visual elements (e.g., services, value objects) here --> </fx:Declarations> <fx:Script> <![CDATA[ [Bindable] public var value:String = "Hello World"; ]]> </fx:Script> <s:VGroup> <s:TextInput id="textInput" change="{value=textInput.text}"/> <s:Label text="{value}"/> </s:VGroup> </s:Application>
2) Two-way binding
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"> <fx:Declarations> <!-- Place non-visual elements (e.g., services, value objects) here --> </fx:Declarations> <s:VGroup> <s:TextInput id="textInput1"/> <s:TextInput text="@{textInput1.text}"/> </s:VGroup> </s:Application>
3) Two-way binding 2
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"> <fx:Declarations> <!-- Place non-visual elements (e.g., services, value objects) here --> </fx:Declarations> <fx:Script> <![CDATA[ [Bindable] private var text:String = ""; ]]> </fx:Script> <s:VGroup> <s:TextInput text="@{text}"/> <s:TextInput text="@{text}"/> </s:VGroup> </s:Application>
2. Five main techniques for user Data Binding.
1) Using braces with MXML tags
2) Using the fx:Binding tag
3) Using the BindingUtils class
4) Implicit and explicit data binding
5) Custom metadata
ps. The above examples are data binding with MXML tags.
3. Data Binding with MXML tags(Braces) in depth
1) Four steps:
1) Generated code
2) Event listeners and handlers
3) Error catching
4) Meta data
2) How compiler works?
1) mxmlc: The mxmlc compiler generates many ActionScript classes (mxml -> as)
2) compc: The compc compiler compiles the mxmlc-generated classes and your classes to create a swf file (as -> swf)
ps. The generated code(.as) are not visiable by default. However, you can instruct the compiler to keep these files by adding a compiler argument -keep or -keep-generated-actionscript=true
Eg.(In _DataBindingUpderTheHoodWatcherSetupUtil.as)
// writeWatcher id=3 shouldWriteSelf=true class=flex2.compiler.as3.binding.PropertyWatcher.. shouldWriteChildren=true watchers[3] = new mx.binding.PropertyWatcher("textInput1", { propertyChange: true } , //.. writeWatcherListeners id=3 size=1 [ bindings[2] ], propertyGetter); // writeWatcher id=4 shouldWriteSelf=true class=flex2.compiler.as3.binding.PropertyWatcher.. shouldWriteChildren=true watchers[4] = new mx.binding.PropertyWatcher("text", { textChanged: true, change: true }, //.. writeWatcherListeners id=4 size=1 [ bindings[2] ], null); // writeWatcher id=0 shouldWriteSelf=true class=flex2.compiler.as3.binding.PropertyWatcher.. shouldWriteChildren=true watchers[0] = new mx.binding.PropertyWatcher("textInput2", { propertyChange: true }, //.. writeWatcherListeners id=0 size=1 [ bindings[0] ], propertyGetter); // writeWatcher id=1 shouldWriteSelf=true class=flex2.compiler.as3.binding.PropertyWatcher.. shouldWriteChildren=true watchers[1] = new mx.binding.PropertyWatcher("text", { textChanged: true, change: true }, //.. writeWatcherListeners id=1 size=1 [ bindings[0] ], null); // writeWatcher id=2 shouldWriteSelf=true class=flex2.compiler.as3.binding.PropertyWatcher.. shouldWriteChildren=true watchers[2] = new mx.binding.PropertyWatcher("text", { propertyChange: true }, //.. writeWatcherListeners id=2 size=1 [ bindings[1] ], propertyGetter);(In dataBindingUnderTheHood-generated.as)
if (_watcherSetupUtil == null) { var watcherSetupUtilClass:Object = getDefinitionByName("_DataBindingUnderTheHoodWatcherSetupUtil"); watcherSetupUtilClass["init"](null); } _watcherSetupUtil.setup(this, function(propertyName:String):* { return target[propertyName]; }, function(propertyName:String):* { return DataBindingUnderTheHood[propertyName]; }, bindings, watchers); mx_internal::_bindings = mx_internal::_bindings.concat(bindings); mx_internal::_watchers = mx_internal::_watchers.concat(watchers);
Comment: As you can see, the magic is actually a lot of code that the mxmlc compiler creates on your behalf. All the code you have seen gets added to your application swf and is an overhead added every time you initialize your application.
4. One and Two-ways Data Binding Using Binding tag
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"> <fx:Declarations> <!-- Place non-visual elements (e.g., services, value objects) here --> </fx:Declarations> <s:VGroup> <s:TextInput id="textInput1"/> <s:TextInput id="textInput2"/> </s:VGroup> <fx:Binding source="textInput1.text" destination="textInput2.text" twoWay="false"/> </s:Application>
ps. You can use <mx:Binding> instead of <fx:Binding>, but it's depreciated.
5. Using the BindingUtils Class
The two techniques you have looked at so far: braces in an MXML tag and the binding tag work well and easy to implement. However, there is overhead associated with these methods. Additionally, you cannot "unbind" properties using these techniques.
There are two ways to use the BindingUtil class.
1) bindProperty() method: The static method is used to bind a public property.
2) bindSetter() method: The static method is used to bind a setter function.
1). bindProperty() function signature:
public static function bindProperty( site:Object, prop:String, host:Object, chain:Object, commitOnly:Boolean = false, useWeakReference:Boolean = false):ChangeWatcher //site: Represents the destination object. //host: Represents the source object. //commitOnly: Set to true in case the handler should be called only on committing change events; set to false in case the handler should be called on both committing and non-committing change events. Default is false. //useWeakReference: Allows you to decide whether the reference to the host is strong or weak. A strong reference (the default) prevents the host from being garbage collected. A weak reference does not.
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768"> <fx:Script> <![CDATA[ import mx.binding.utils.BindingUtils; import mx.events.FlexEvent; import mx.binding.utils.ChangeWatcher; private var change:ChangeWatcher; protected function preinitializeHandler(event:FlexEvent):void { change = BindingUtils.bindProperty(simpleText, "text", textInput, "text"); } protected function clickHandler(event:MouseEvent):void { change.unwatch(); change = null; } ]]> </fx:Script> <s:layout> <s:VerticalLayout /> </s:layout> <s:TextInput id="textInput" preinitialize="preinitializeHandler(event)" /> <s:SimpleText id="simpleText" /> </s:Application>
2) bindSetter() function signature
public static function bindSetter(setter:Function, host:Object, chain:Object, commitOnly:Boolean = false, useWeakReference:Boolean = false):ChangeWatcher // setter: Bind a setter function to a bindable property or chain. Once the ChangeWatcher instance is successfully created, the setter function is invoked with the value or the chain object. // host: Represents the source object. // chain: Represents the property name. // commitOnly : Set to true in case the handler should be called only on committing change events; set to false in case the handler should be called on both committing and non-committing change events. Default is false. //useWeakReference: Allows you to decide whether the reference to the host is strong or weak. A strong reference (the default) prevents the host from being garbage collected. A weak reference does not.
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768"> <fx:Script> <![CDATA[ import mx.binding.utils.BindingUtils; import mx.events.FlexEvent; protected function preinitializeHandler(event:FlexEvent):void { BindingUtils.bindSetter(bindingSetter, textInput, "text"); } private function bindingSetter(str:String):void { label.text = str; } ]]> </fx:Script> <s:layout> <s:VerticalLayout /> </s:layout> <s:TextInput id="textInput" preinitialize="preinitializeHandler(event)" /> <s:Label id="label" /> </s:Application>
Comments: BindingUtils class methods are quicker than the MXML braces because the class doesn't generate all the additional code that the compiler added.