Programmatic Bindings
Recently I found myself refactoring an MXML component into an AS base class to make it easier to provide multiple views of the data. I wanted to factor as much of the code as I could into the base class including some of the bindings. While I've known for some time that Flex has programmatic binding support I'd not had a real use for it until now. The documentation is reasonable but I think I couple more examples would have helped. Below is a sample binding and the corresponding programmatic equivalent along with some gotchas.
Take an example MXML component that has a model Object set on it. The model exposes an enabled flag which the view needs to respond to in a complex fashion, beyond just disabling itself. If you want you can also replace the setter with a variable in these examples and it all works the same. It might look something like this (a bunch of code has been removed to focus just on the binding):
<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script><![CDATA[ import com.example.Model; [Bindable] public var model:Model; private function set controlsEnabled(controlsEnabled:Boolean):void { // logic to handle change } ]]></mx:Script> <mx:Binding source="model.enabled" destination="controlsEnabled"/> <!-- child components removed --> </mx:Canvas>
Now I want to turn that into an AS base class and not have every MXML subclass need to repeat the binding. My AS base class then looks like this:
package com.example { import mx.binding.utils.BindingUtils; import mx.containers.Canvas; public class SimpleBase extends Canvas { [Bindable] public var model:Model; public function SimpleBase() { super(); BindingUtils.bindProperty(this, "controlsEnabled", this, ["model", "enabled"]); } public function set controlsEnabled(controlsEnabled:Boolean):void { // logic to handle change } } }
While the view has changed to this:
<?xml version="1.0" encoding="utf-8"?> <SimpleBase xmlns="com.example.*" xmlns:mx="http://www.adobe.com/2006/mxml"> <!-- child components removed --> </SimpleBase>
The key to this magic is the BindingUtils.bindProperty() call in the base class. That mimics the mx:Binding that was previously in the MXML code. The documentation for BindingUtils uses site and host instead of source and destination which means you need to do a little translation when converting from one to the other. The most important thing to keep in mind when doing a translation like the one above is the use of this for both the site and host, otherwise you won't pick up all the changes.
The reason is that the bindings need to be anchored on some object that is issuing property change events. In the SimpleBase constructor model is null, so we can't really bind to that. Even if it wasn't null, the bindings would only fire off of the model instance that was active when it was setup. By chaining off of this you pick up changes both to model and the enabled flag within the model object.
The other tricky thing to notice is that the access modifier of the controlsEnabled() method changed from private to public. This needs to happen so that the code in BindingUtils can call it. Since this code is being executed outside of the class when in AS, instead of being compiled in (which is the case with MXML) it needs to be able to have standard access to the method. If you forget to make the setter public you'll get an error like this:
ReferenceError: Error #1056: Cannot create property controlsEnabled on SimpleView2.
The BindingUtils.bindSetter() method allows you to call an arbitrary method with the new value instead of just setting a variable (directly or via a setter).
Comments
Posted by: Maikel Sibbald | March 3, 2007 3:59 AM