So You Think You Can Bind? – A Flex Puzzler

February 19th, 2009 Brian Gray, Consultant  (email the author)

This entry is part 3 of 6 in the series Flex Development

In my opinion, binding is one of the trickiest concepts for a Java developer to really understand when first learning Flex.  It’s not because it is overly complex, but because binding is similar enough to other concepts in JSP, Swing, etc. that we begin to form mental models without fully understanding the differences.  But those subtle differences are important for the last 20% of correctness in a lot of code.

The Puzzle

To illustrate, I cooked up a Java Puzzler-esque Binding puzzle.  The application below contains a panel with a button on it.  It has 2 states.  Clicking the button switches to the ‘large’ state, at which point the panel widens and the button’s label changes to “Contract…” instead of “Expand….”

So go ahead and click the button, then see if you can answer this question: Why doesn’t the text change?

Source sample: (see link at end of post for full source)

<mx:states>
    <mx:State name="large">
        <mx:SetProperty target="{expandBtn}" name="label"
            value="Contract the panel from width: {panel.width}" />
        <mx:SetProperty target="{panel}" name="width" value="430"/>
    </mx:State>
</mx:states>

<mx:Panel id="panel" width="330" title="Binding">
    <mx:Button id="expandBtn"
        label="Expand the panel from width: {panel.width}"
        click="currentState='large'" />
</mx:Panel>

The Solution(s)

The answer is an explanation of what is really going on when you use the {} notation in mxml.  When the MXML compiler sees a line like:

label="Expand the panel from width: {panel.width}"

it creates a binding.  More importantly for this problem, it creates a ChangeWatcher, essentially telling Flex, “Any time that the width of the panel changes, call label’s setter using the updated value.”  In this case, the updated value is “Expand the panel from width foo” where foo is the new width.

In most cases, this is exactly what the developer intends to happen.  Complications arise when code elsewhere (either in an <mx:state> tag states as above or in scripts, other classes, other components, other bindings, etc.) also sets this same value.  Why is this a problem?  That ChangeWatcher that gets generated – it sticks around.  The user clicks the button, the text of the label changes, but the original change watcher is still there.  He’s still hanging out, listening to the panel width, and waiting to pounce.

Continuing the story, the panel width gets updated, and the change watcher from the original label springs into action.  It wakes up and says, “Ah hah! My subject has changed, so I must update my bound value.”  It sets the label to the only thing it knows to set it to, namely “Expand the panel from width foo.”

There are a couple ways to solve the problem that reveal some best practices dealing with binding in Flex.  Here’s one I’ll talk about in a bit of detail:

The idea here is to not to change the label itself, since it’s going to have change watchers calling its setter.  Using this new language of binding, you can think about this solution as follows.  The label is now set to what is essentially a function with two parameters.  One is the verb to be used (“Expand” or “Contract”) and the other is the width of the panel.  This way, two change watchers are created, and any time either value changes, Flex will re-evaluate the expression and set the label text to the result.

Source sample: (see link at end of post for full source)

<mx:Script>
    <![CDATA[
        [Bindable]
        public var verb:String = "Expand";
    ]]>
</mx:Script>

<mx:states>
    <mx:State name="large">
        <mx:SetProperty target="{this}" name="verb" value="Contract" />
        <mx:SetProperty target="{panel}" name="width" value="430"/>
    </mx:State>
</mx:states>

<mx:Panel id="panel" width="330" title="Binding">
    <mx:Button id="expandBtn"
        label="{verb} the panel from width: {panel.width}"
        click="currentState='large'"  />
</mx:Panel>

Another possibility is to do no binding at all.  You could set the label when the button initializes based on the current width of the panel, and change it in the state itself since you know that’s when the panel also changes width.

The Lesson

The take-away here is the same in both solutions: know who is calling your setters.  Generally, if you assign a value using the {} notation, that should be the only place that value gets set.  Otherwise, you enter into a logic puzzle when an unexpected value appears – was it the change watcher from the generated binding, the ActionScript at the top of the MXML, other components accessing public properties, …?

Download all source files

Entry Filed under: Agile and Development

1 Comment Add your own

  • 1. James Ward  |  February 20th, 2009 at 2:39 pm

    Cool puzzler! I’ve wanted to someday put together a Flex Puzzlers session like the Java Puzzlers one. If so, can I use this? (giving you credit of-course!)

    -James

Leave a Comment

Required

Required, hidden


+ 7 = twelve

Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Trackback this post  |  Subscribe to the comments via RSS Feed

© 2010-2012 Summa All Rights Reserved -- Copyright notice by Blog Copyright