Flex data binding pitfalls

Flex data binding provides a powerful mechanism for reacting to data changes, but it can be exceptionally tricky to debug when it goes wrong. This post is an accumulation of advice gleaned from colleagues, blogs and painful personal experience into how to avoid some of the nastiest of binding woes.

The best place to start is to read Chapter 40 (Data Binding) of the Adobe Flex 3 developer guide; a great introduction to the topic. I’m taking this as assumed reading, so won’t be covering the many tips contained therein.

Let’s jump straight into the gory details…

1) Don’t use constants as Bindable event names.

public static const ASK_PRICE_CHANGE : String =
   "ASK_PRICE_CHANGE";

// DON'T DO THIS!!!!!

[Bindable(event=ASK_PRICE_CHANGE)]
public function get askPrice():Number
{
	return _askPrice;
}
public function set askPrice(value : Number) : void
{
	if (value != _askPrice) {
		_askPrice = value;
		dispatchEvent(new Event(ASK_PRICE_CHANGE));
	}
}

It’s a great idea in theory, but it doesn’t work. It looks like you’re using the constant here, but it’s just a literal string. If you change the constant value, the binding will not fire.

2) Don’t rely on the execution order of bindings.
You can’t rely on the order staying the same, even if it works for you now. A refactoring of your source file, or changes to the behaviour of future Flex compilers could cause your assumption to fail in future. Here’s a good example:

<mx:Panel id="foo"
   visible="{model.canViewFoo}"
   includeInLayout="{foo.visible}">
...
</mx:Panel>

This worked most of the time in Flex 3.0, but fails very frequently in Flex 3.3. The right way to do it is:

<mx:Panel id="foo"
   visible="{model.canViewFoo}"
   includeInLayout="{model.canViewFoo}">
...
</mx:Panel>

3) Be wary of swallowed exceptions
Exceptions thrown in binding expressions, or in functions called within binding expressions are silently captured by the binding framework.

4) [Bindable(“propertyChange”)] is not the same as [Bindable]
Even though behind the scenes, the default event name is “propertyChange”, if you explicitly refer to it in your [Bindable] tag, the compiler assumes you will be generating the event yourself. If other properties in the same class are bindable, their updates will not trigger a binding update on your property too. Worse yet, if you try and use dispatchEvent(new Event("propertyChange")) in your setter, you will end up with type coercion errors as the binding framework expects events with the name “propertyChange” to actually be of type PropertyChangeEvent. If you actually dispatch a PropertyChangeEvent event, all will be well, however you might as well of just labelled the property [Bindable] in the first place and let the code generator handle the donkey work.

5) Take extra care when using interfaces in binding expression
If you refer to your bindable class via an interface in a binding expression, you need to be aware of the following limitations:

  1. If the [Bindable] event name in your interface is different from the [Bindable] event name in your class, binding events will not fire.
  2. If you have both a getter and setter for a property and no [Bindable] tag in your interface, binding events will not fire.
  3. If you have only a getter for your property and no [Bindable] tag in your interface, binding events will fire. This is just a weird artifact and shouldn’t be relied upon in future.
  4. Be extra extra careful with nested interfaces.

If you wish to refer to your domain objects using interfaces in classes that use bindings, make sure the bindings metadata exactly matches. If you have multiple implements of an interface with different [Bindable] tags, don’t use the interface for binding expressions, as chances are, it won’t work. Instead, use Object and all your different binding implementations will work as expected.

Debugging bindings
When bindings do go wrong, what’s the easiest way to debug them? You can view the generated source, but don’t expect it to be especially enlightening. About the only useful thing you will glean from perusing the source, is to marvel at just how much of it there is.
A more constructive way to debug is to use the undocumented BindingManager class. This shows you when each of your bindings fire. Add the following line to your Application…

BindingDebugger.debugComponent(this);

…and include the following class…

package
{
import mx.binding.BindingManager;
import mx.binding.IBindingClient;
import mx.core.UIComponent;
import mx.core.mx_internal;

use namespace mx_internal;

// Kudos to Cathal Golden for coming up with this technique
public class BindingDebugger
{

private static const BINDINGS_PROPERTY : String =
“_bindingsByDestination”;

public static function debugComponent (
displayObject : Object, recursive : Boolean = true) : void
{
if (displayObject is IBindingClient)
{
var bindings : Object = displayObject[ BINDINGS_PROPERTY ];
for ( var binding : String in bindings )
{
BindingManager.debugBinding(binding);
}
}

if (recursive && displayObject is UIComponent)
{
var component : UIComponent = UIComponent( displayObject );
for (var i : int = 0; i < component.numChildren; i++) { debugComponent(component.getChildAt(i), true); } } } } } [/sourcecode] ...and you should see all your binding expressions traced out to the console with output like this:

Binding: destString = bid.text, srcFunc result = Bid $15.31
Binding: destString = calcSpread.text, srcFunc result = Calc Spread $2.79
Binding: destString = ask.text, srcFunc result = Ask $18.12

Next post, I’ll look at ways to optimize data binding when dealing with low latency streaming data.

7 thoughts on “Flex data binding pitfalls

  1. Hi Andy,

    Some really useful information you present here. I actually don’t understand your first issue:
    “Don’t use constants as Bindable event names”

    I usually do this and it works. So what do you mean by: ‘If you change the constant value, the binding will not fire.’ ?

    I guess you mean changing the constant value at run-time, but why would you change a constant value at run-time?

    Thanks,

    Eugene

  2. Hi Eugene,

    public static const ASK_PRICE_CHANGE : String =  
       "askPriceChange";
    
    [Bindable(event=ASK_PRICE_CHANGE)]
    public function get askPrice():Number
    

    The above code doesn’t work since the bindable tag causes the compiler to generate binding code listening for events of type “ASK_PRICE_CHANGE”, not type “askPriceChange” as you might expect. Try looking at the generated binding source files to see what I mean.

  3. Hi Andy,
    I recently read your blog post on Data Binding Pitfalls and really liked it.

    We’re trying to add more links in our docs so that Flex developers are made aware of the great community content out there. I’m wondering whether you would consider adding a comment with a link to your post to the following page:

    * Your page: https://dodgybits.org/2009/04/12/flex-data-binding-pitfalls/
    * Our page: http://livedocs.adobe.com/flex/3/html/help.html?content=data_access_1.html

    Integration of customer-generated content is a key component in the success of the Flash platform documentation, so we really appreciate your contribution. FYI, here’s a blog post with a little more information about community help contributions: http://blogs.adobe.com/actionscriptdocs/2009/08/developers_and_writers_adobe_w.html. This post specifically mentions Adobe AIR but we’re doing this across the entire Adobe Flash Platform.

    Best regards
    Randy Nielsen
    Flex Documentation Manager
    Adobe Systems Incorporated

  4. Hi Randy,
    Thanks for this nice writeup. Could you perhaps discuss the meaning and implications of the following warning that is seen when attempting to watch or bind a setter to a property such as ‘enabled’ on a Button control?

    ‘warning: multiple describeType entries for ‘[PROPERTY]’ on type…’

    Thanks!

    • Hi Daniel,

      FYI I am running into the same warning. There seems to be a lot of confusion about what this warning means.

      Thanks,

      Philip

Leave a reply to Daniel Cancel reply