Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Diving in the Flex Data Binding Waters


Published on

In depth overview of the Flex data binding code generation. Provides info on accomplish data binding through actionscript as well as limitations of the process.

Published in: Business, Economy & Finance
  • richard,

    When you mark a class or a specific property/getter/setter as [Bindable], it basically marks that item for code generation via the pre-compiler before the compiler does it's thing with the code. The dispatchEvent(...) is automatically generated.

    When you say [Bindable(event='blah')] or [Bindable(event='propertyChanged')], the pre-compiler says, wow, this guy knows what he is doing, and expects you to dispatch the event.

    This has nothing to do with the performance implications (e.g. 5000 bindable variables in a model locator all dispatching the same event) where your 'custom' name just happens to be the auto-generated event name.

    Hope that helps
    Are you sure you want to  Yes  No
    Your message goes here
  • So if one uses '[Bindable]' on the accessor/mutator, the compiler will always dispatch a propertyChanged event (i.e. without checking to see if the value has actually changed), but if one specifies the event in the Bindable metatag (even if the specified event is 'propertyChanged'), then the mutator is responsible for dispatching the event?

    The mutator seems to call its matching accessor automatically; does this have anything to do with a binding optimization ?

    Are you sure you want to  Yes  No
    Your message goes here
  • great presentation!
    An important part is maybe coming a bit too short in the slides: Performance.
    Binding with MXML is cool but much slower then using BindingUtils (~50%)...
    Are you sure you want to  Yes  No
    Your message goes here

Diving in the Flex Data Binding Waters

  1. 1. Diving in the Data Binding Waters Michael Labriola Digital Primates
  2. 2. Who are you? <ul><li>Michael Labriola </li></ul><ul><li>Senior Consultant at Digital Primates </li></ul><ul><li>Flex Geek </li></ul><ul><li>Component Developer </li></ul><ul><li>Flex Team Mentor </li></ul>
  3. 3. Who were you? <ul><li>Michael Labriola </li></ul><ul><li>Software Engineer </li></ul><ul><li>Embedded Systems Developer </li></ul><ul><li>Reverse Engineer </li></ul>
  4. 4. What is this session about? <ul><li>This session is part of my continuing quest to teach Flex from the inside out. </li></ul><ul><li>Learn what the Flex framework is really doing and you are more likely to use it successfully, respect its boundaries and extend it in useful ways </li></ul>
  5. 5. One more reason <ul><li>Let’s call it “Game Theory”. </li></ul><ul><li>If you know how something works really well, you know which rules you can bend and just how far you can bend them before they break. </li></ul><ul><li>Sometimes you can even find really creative ways out of difficult situations </li></ul>
  6. 6. Standard Disclaimer <ul><li>I am going to lie to you a lot… a whole lot </li></ul><ul><li>Even at this ridiculous level of detail, there is much more </li></ul><ul><li>All of this is conditional. So, we are just going to take one route and go with it </li></ul>
  7. 7. Data Binding Defined <ul><li>Data Binding is the magical process by which changes in a data model are instantly propagated to views. </li></ul>
  8. 8. Now Really Defined <ul><li>Data Binding is not magic </li></ul><ul><li>It is a relatively complicated combination of generated code, event listeners and handlers, error catching and use of meta data through object introspection </li></ul>
  9. 9. Still on the Soap Box <ul><li>Data Binding works because Flex (which I am generically using here to mean precompiler, compiler and framework) generates a lot of code on your behalf. </li></ul>
  10. 10. Transformation <ul><li>When you use Data Binding, the Flex compiler generates code for you.. A lot of code </li></ul><ul><li>So, the following example becomes.. </li></ul><ul><li>package valueObject { </li></ul><ul><li>[Bindable] </li></ul><ul><li>public class Product { </li></ul><ul><li>public var productName:String; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  11. 11. Generated Code <ul><li>p ackage valueObject { </li></ul><ul><li>import; </li></ul><ul><li>public class ProductManualBinding implements IEventDispatcher { </li></ul><ul><li> private var = new; </li></ul><ul><li> [Bindable(event=&quot;propertyChange&quot;)] </li></ul><ul><li> public function get productName():String { </li></ul><ul><li> return _productName; </li></ul><ul><li> } </li></ul><ul><li> public function set productName(value:String):void { </li></ul><ul><li> var oldValue:Object = _productName; </li></ul><ul><li> if (oldValue !== value) { </li></ul><ul><li> _productName = value; </li></ul><ul><li> dispatchEvent(, &quot;productName&quot;, oldValue, value)); </li></ul><ul><li> } </li></ul><ul><li> } </li></ul><ul><li> public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, weakRef:Boolean = false):void { </li></ul><ul><li> dispatcher.addEventListener(type, listener, useCapture, </li></ul><ul><li> priority, weakRef); </li></ul><ul><li> } </li></ul><ul><li> public function dispatchEvent( { </li></ul><ul><li> return dispatcher.dispatchEvent(event); </li></ul><ul><li> } </li></ul><ul><li> public function hasEventListener(type:String):Boolean { </li></ul><ul><li> return dispatcher.hasEventListener(type); </li></ul><ul><li> } </li></ul><ul><li> public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void { </li></ul><ul><li> dispatcher.removeEventListener(type, listener, useCapture); </li></ul><ul><li> } </li></ul><ul><li> public function willTrigger(type:String):Boolean { </li></ul><ul><li> return dispatcher.willTrigger(type); </li></ul><ul><li> } </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  12. 12. Most Importantly <ul><li>public class ProductManualBinding implements IEventDispatcher { </li></ul><ul><li> private var dispatcher:EventDispatcher = new EventDispatcher(IEventDispatcher(this)); </li></ul><ul><li> [Bindable(event=&quot;propertyChange&quot;)] </li></ul><ul><li> public function get productName():String { </li></ul><ul><li> return _productName; </li></ul><ul><li> } </li></ul><ul><li> public function set productName(value:String):void { </li></ul><ul><li> var oldValue:Object = _productName; </li></ul><ul><li> if (oldValue !== value) { </li></ul><ul><li> _productName = value; </li></ul><ul><li> dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, &quot;productName&quot;, oldValue, value)); </li></ul><ul><li> } </li></ul><ul><li> } </li></ul>
  13. 13. Only Half of the Equation <ul><li>Data Binding is about changing a model and having the view react </li></ul><ul><li>So, the generated code for the following view becomes… </li></ul><ul><li>[Bindable] </li></ul><ul><li>private var product:Product; </li></ul><ul><li><mx:Label id=&quot;myLbl&quot; text=&quot;{product.productName}&quot;/> </li></ul>
  14. 14. The Other 7/8 <ul><li>override public function initialize():void { </li></ul><ul><li>var bindings:Array = []; </li></ul><ul><li>var binding:Binding; </li></ul><ul><li>binding = new mx.binding.Binding(this, </li></ul><ul><li>function():String { </li></ul><ul><li>var result:* = (product.productName); </li></ul><ul><li>var stringResult:String = (result == undefined ? null : String(result)); </li></ul><ul><li>return stringResult; </li></ul><ul><li>}, </li></ul><ul><li>function(_sourceFunctionReturnValue:String):void { </li></ul><ul><li>myLabel.text = _sourceFunctionReturnValue; </li></ul><ul><li>}, </li></ul><ul><li>&quot;myLabel.text&quot;); </li></ul><ul><li>bindings[0] = binding; </li></ul><ul><li>var watchers:Array = []; </li></ul><ul><li>watchers[0] = new mx.binding.PropertyWatcher(&quot;product&quot;,{propertyChange: true},[ bindings[0] ], function(propertyName:String):* { return target[propertyName]; } ); </li></ul><ul><li>watchers[1] = new mx.binding.PropertyWatcher(&quot;productName&quot;,{productNameChanged: true}, [bindings[0]],null); </li></ul><ul><li>watchers[0].updateParent(target); </li></ul><ul><li>watchers[0].addChild(watchers[1]); </li></ul><ul><li>for (var i:uint = 0; i < bindings.length; i++) { </li></ul><ul><li>Binding(bindings[i]).execute(); </li></ul><ul><li>} </li></ul><ul><li>mx_internal::_bindings = mx_internal::_bindings.concat(bindings); </li></ul><ul><li>mx_internal::_watchers = mx_internal::_watchers.concat(watchers); </li></ul><ul><li>super.initialize(); </li></ul><ul><li>} </li></ul>
  15. 15. Starting at the Top <ul><li>The generated code overrides the initialization function to add all of the generated code into startup </li></ul><ul><li>The first relevant thing it does for us it to create an Array of mx.binding.Binding objects. These objects are responsible for executing bindings.. (moving values from the binding source to the destination.) </li></ul>
  16. 16. mx.binding.Binding <ul><li>Instances of this class accept a document, srcFunc, destFunc and destString as parameters. </li></ul><ul><li>The document is the target of the work. The srcFunc returns the value used in the binding. The destFunc assigns it to the destination. The destString is the destination represented as a String… more on that later </li></ul>
  17. 17. Binding in our Example <ul><li>var bindings:Array = []; </li></ul><ul><li>var binding:Binding; </li></ul><ul><li>binding = new mx.binding.Binding(this, </li></ul><ul><li>function():String { </li></ul><ul><li>var result:* = (product.productName); </li></ul><ul><li>var stringResult:String = (result == undefined ? null : String(result)); </li></ul><ul><li>return stringResult; </li></ul><ul><li>}, </li></ul><ul><li>function(_sourceFunctionReturnValue:String):void { </li></ul><ul><li>myLabl.text = _sourceFunctionReturnValue; </li></ul><ul><li>}, </li></ul><ul><li>“ myLbl.text&quot;); </li></ul><ul><li>bindings[0] = binding; </li></ul>
  18. 18. Watchers <ul><li>Still in the initialize method, the generated code creates an array of mx.binding.PropertyWatcher objects </li></ul><ul><li>The objects are responsible for noticing a change and, among other things, notifying the binding objects that they should execute </li></ul>
  19. 19. mx.binding.PropertyWatcher <ul><li>Instances of this class accept the propertyName, an object that indicates which events are broadcast when the property has changed, an array of listeners and a propertyGetter function </li></ul><ul><li>The listeners are any Binding instances created for the property. In this case, the property getter is an anonymous function that returns the value of the property binding. </li></ul>
  20. 20. Watchers in our Example <ul><li>watchers[0] = new mx.binding.PropertyWatcher(&quot;product&quot;, </li></ul><ul><li>{propertyChange: true},[ bindings[0] ], propertyGetter ); </li></ul><ul><li>watchers[1] = new mx.binding.PropertyWatcher(&quot;productName&quot;, </li></ul><ul><li>{productNameChanged: true}, [bindings[0]],null); </li></ul><ul><li>watchers[0].updateParent(target); </li></ul><ul><li>watchers[0].addChild(watchers[1]); </li></ul>
  21. 21. Chains <ul><li>Data Binding Expressions are rarely simple names of properties. They are often chains. </li></ul><ul><li>For example: </li></ul><ul><li><mx:Text id=&quot;myText&quot; text=&quot;{}&quot;/> </li></ul>
  22. 22. Execution <ul><li>After the watchers are setup, the generated initialize function loops through all of the Binding objects and calls their execute() method. </li></ul><ul><li>This method cautiously attempts to set the destination value to the source value, first ensuring that we aren’t already in process or an in an infinite loop. </li></ul>
  23. 23. Value Changed <ul><li>One important thing to note about this process which often trips up new users to databinding: </li></ul><ul><li>A value on an object is only set if it is differnet </li></ul><ul><li>(oldValue !== value) </li></ul><ul><li>What impact does this have on Objects? Arrays? </li></ul>
  24. 24. Ways to Bind <ul><li>This explains how binding is setup if the bindings are declared in MXML. There are ways to handle binding in ActionScript: </li></ul><ul><li>mx.binding.utils.BindingUtils </li></ul><ul><li>mx.binding.utils.ChangeWatcher. </li></ul><ul><li>Manually adding event listeners </li></ul>
  25. 25. The differences <ul><li>You cannot: </li></ul><ul><li>include ActionScript code in a data binding expression defined in ActionScript. </li></ul><ul><li>include an E4X expression in a data binding expression defined in ActionScript. </li></ul><ul><li>include functions or array elements in property chains in a data binding expression defined this way </li></ul>
  26. 26. Also <ul><li>MXML provides better warning and error detection than any of the runtime methods </li></ul>
  27. 27. BindingUtils <ul><li>BindingUtils provides two methods which do this work for you at runtime </li></ul><ul><li>bindProperty and bindSetter </li></ul><ul><li>The first one is used with public properties. The second is used with getter/setters. </li></ul>
  28. 28. bindProperty Syntax <ul><li>public static function bindProperty(site:Object, prop:String, host:Object, chain:Object, commitOnly:Boolean = false):ChangeWatcher </li></ul><ul><li>For example: </li></ul><ul><li>public function setup():void { </li></ul><ul><li>BindingUtils.bindProperty(someOtherTextFiled, “text”, someTextInput, &quot;text&quot;); </li></ul><ul><li>} </li></ul>
  29. 29. bindSetter Syntax <ul><li>public static function bindSetter(setter:Function, host:Object, chain:Object, commitOnly:Boolean = false):ChangeWatcher </li></ul><ul><li>For example: </li></ul><ul><li>public function updateIt(val:String):void { </li></ul><ul><li>someOtherTextFiled.text = val.toUpperCase(); </li></ul><ul><li>} </li></ul><ul><li>public function setup():void { </li></ul><ul><li>BindingUtils.bindSetter(updateIt, someTextInput, &quot;text&quot;); </li></ul><ul><li>} </li></ul>
  30. 30. The Chain <ul><li>The chain is a rather complex parameter that can take on multiple forms… for instance, it can be a: </li></ul><ul><li>String containing the name of a public bindable property of the host object. </li></ul><ul><li>An Object in the form: { name: property name, getter: function(host) { return host[property name] } }. </li></ul><ul><li>A non-empty Array containing a combination of the first two options that represents a chain of bindable properties accessible from the host. For example, to bind the property host.a.b.c, call the method as: bindProperty(host, [&quot;a&quot;,&quot;b&quot;,&quot;c&quot;], ...). </li></ul>
  31. 31. ChangeWatcher <ul><li>public function setup():void { </li></ul><ul><li>, &quot;text&quot;, watchMeAndReact); </li></ul><ul><li>} </li></ul><ul><li>public function watchMeAndReact(event:Event):void{ </li></ul><ul><li>myTA1.text=&quot;done&quot;; </li></ul><ul><li>} </li></ul><ul><li>You can also unwatch() something.. </li></ul>
  32. 32. Manual Event Listeners <ul><li>You could, but… </li></ul><ul><li>The data binding code swallows a bunch of errors on your behalf, to handle things like null values, etc… your code will crash if you don’t take the same care </li></ul>
  33. 33. What does this mean? <ul><li>Binding is just a contract between two objects. </li></ul><ul><li>One object explains that it will broadcast an event when it changes and details what event that will be </li></ul><ul><li>Another object waits for that event to occur and updates the destination when it occurs </li></ul>
  34. 34. propertyChange <ul><li>Even though the propertyChange is the default event that Flex uses when you auto-generate binding code, you can change it if you use your own getters and setters. </li></ul><ul><li>For example: </li></ul>
  35. 35. Not propertyChange <ul><li>private var _productName:String; </li></ul><ul><li>[Bindable(event='myProductNameChanged')] </li></ul><ul><li>public function get productName():String { </li></ul><ul><li>return _productName; </li></ul><ul><li>} </li></ul><ul><li>public function set productName( value:String ):void { </li></ul><ul><li>_productName = value; </li></ul><ul><li>dispatchEvent( new Event('myProductNameChanged') ); </li></ul><ul><li>} </li></ul>
  36. 36. Not getter/Setter <ul><li>You will need to broadcast this event somewhere else. </li></ul><ul><li>[Bindable(event='myProductNameChanged')] </li></ul><ul><li>public var productName:String; </li></ul>
  37. 37. Double Down <ul><li>private var _productName:String; </li></ul><ul><li>[Bindable(event='serverDataChanged')] </li></ul><ul><li>[Bindable(event='myProductNameChanged')] </li></ul><ul><li>public function get productName():String { </li></ul><ul><li>return _productName; </li></ul><ul><li>} </li></ul><ul><li>public function set productName( value:String ):void { </li></ul><ul><li>_productName = value; </li></ul><ul><li>dispatchEvent( new Event('myProductNameChanged') ); </li></ul><ul><li>} </li></ul>
  38. 38. Models, Oh Models <ul><li>The propertyChange event is broadcast by default for every property setter that is auto-generated </li></ul><ul><li>How do you think that scales in a giant application model? </li></ul><ul><li>What happens? </li></ul>
  39. 39. Binding to Unbindable <ul><li>Putting some of this to use </li></ul>
  40. 40. Lazy Load <ul><li>Putting some of this to use </li></ul>
  41. 41. Random Closing Tips <ul><li>Any users of describeType out there…. Make sure you use the DescribeTypeCache </li></ul><ul><li>var info:BindabilityInfo = </li></ul><ul><li>DescribeTypeCache.describeType(parentObj). </li></ul><ul><li>bindabilityInfo; </li></ul>
  42. 42. Q & A <ul><li>Seriously? You must have some questions by now? </li></ul>
  43. 43. Resources <ul><li>Blog Aggregator (All of the Digital Primates) </li></ul><ul><li> </li></ul><ul><li>My Blog Specifically </li></ul><ul><li> </li></ul>