Flex two-way data binding head slap

Everyone once in a while you come across something that makes such perfectly good sense you slap your head and say "why didn't I think of that" (or maybe you slam your head off your desk). I just had one of those moments yesterday while reading through some sample source code.

The Flex [Bindable] tag is something I use all the time. Building data and form heavy apps makes the whole concept of data binding one of my favourite features in the the Flex framework, however, I have always had one beef with databinding. Two-way databinding was a pain to setup and looked plain ugly. You had to either use the BindingUtils or the mx:Binding tag or have listeners setup for value changes, argg. A simple example will drive home the point.

This is the model (value object) that I want to bind to a form:

 [Bindable]
public class LocationVO {
public var id:String = "0";
public var name:String = "";
public var address1:String = "";
public var address2:String ="";
public var city:String = "";
public var state:String = "";
public var zip:String = "";
public var phone:String = "";

public function LocationVO() {
}
}

Nothing special, the whole class is marked bindable so any public property can be bound to.

Next you have the view. Somewhere in the mx:Script tag you would declare a variable,in this instance called 'location' and you would mark it bindable with the [Bindable] tag. Below is the form that the object is bound to, so far nothing out of the ordinary.

<mx:Script>
<![CDATA[

import com.dgrigg.model.LocationVO;

[Bindable]
protected var location:LocationVO;

]]>
</mx:Script>
<mx:Form>
<mx:FormItem label="Name:">
<mx:TextInput id="nameTxt" text="{location.name}"/>
</mx:FormItem>

<mx:FormItem label="Address:">
<mx:TextInput id="address1Txt" text="{location.address1}"/>
</mx:FormItem>

<mx:FormItem>
<mx:TextInput id="address2Txt" text="{location.address2}"/>
</mx:FormItem>

<mx:FormItem label="City:">
<mx:TextInput id="cityTxt" text="{location.city}"/>
</mx:FormItem>

<mx:FormItem label="State:" >
<mx:ComboBox id="stateCmb"
prompt="Select state"
dataProvider="{Constants.STATES.state}" labelField="@label"/>
</mx:FormItem>

<mx:FormItem label="Zipcode:">
<mx:TextInput id="zipTxt" text="{location.zip}"/>
</mx:FormItem>

<mx:FormItem label="Phone:">
<mx:TextInput id="phoneTxt" text="{location.phone}"/>
</mx:FormItem>
</mx:Form>

 Where it gets a little ugly is in getting the changes to the text fields back into the bound object. Generally you need to do something like this in the view, not awful but not very clean.

<mx:Binding source="nameTxt.text" destination="location.name" />
<mx:Binding source="address1Txt.text" destination="location.address1"/>
<mx:Binding source="address2Txt.text" destination="location.address2"/>
<mx:Binding source="cityTxt.text" destination="location.city"/>
<mx:Binding source="zipTxt.text" destination="location.zip"/>
<mx:Binding source="phoneTxt.text" destination="location.phone"/>

Personally I have always hated that big block of mx:Binding tags. So now you are asking what caused the great head slap. Here it is.

It never dawned on me to create an instance of the object using mxml tags. Much like creating an instance of an xml object using the mx:XML tag you can create an instance of any value object using tags, you just need to declare the namespace in the component header. After that you can do this:

<model:LocationVO id="location"
name="{nameTxt.text}"
address1="{address1Txt.text}"
address2="{address2Txt.text}"
city="{cityTxt.text}"
zip="{zipTxt.text}"
state="{stateCmb.selectedItem.@data}"
phone="{phoneTxt.text}"/>

What this accomplishes is a few things. First, I don't need to declare and create an instance of the value object in the mx:Script tag block. Second, I don't need to remember to mark it as bindable. And third, I don't need to use all those mx:Binding tags. Now I just need the snippet above and my form with the corresponding {} bindings in each form item.

Flex 4 changes all this with proper two-way data binding which will be really nice. Unfortunately Flex 4 isn't quite ready for showtime yet, so in the interm this is a great step to get clean quasi two-way data binding.


9 Comments:

  1. Scott

    Another way I have used (aka another ugly hack (with less typing) until Flex 4 is in the wild) is to use the valueCommit method of the evil TextInput component to create true way binding. So, something like this.

  2. Scott

    <pre><mx:TextInput id="zipTxt" text="{location.zip}" valueCommit="{location.zip = zipTxt.text}" /></pre>

    Last try I swear!

  3. Dan

    Thanks for sharing. I never really thought about using my vo in mxml like that. You just see so many examples of a vo written in script.

  4. Steven

    How would you handle binding an Array or pre-setting a combo box? I couldn't get it to work individually adding properties, I had to just do the VO name:


    But I have two properties in my VO where one is just an area_id:int and the other is an Array of SubVO. The area_id binds to a combo box which is an ArrayCollection, where the area_id is a property of each VO in the collection.

    Can you use your solution with ArrayCollections and combo boxes?

  5. Derrick

    Steven,

    Flex has no built in way to set the mx:combobox to a bound value. Generally I have a method where I set the value object or replace the existing one and right after that I have a util class that takes a reference to the combo box and any value I pass it. It loops over the dataProvider of the combo box looking for a match in the data field and then sets the combo box to that item. It's not as nice as pure binding but it does the trick.

  6. elmandelsaco

    GREAT POST!

    Always wanted to find a way to make this binding clean within the view. Now I got it courtesy of Mr Grigg...

    Thanks!

  7. Phil Herold

    So how do you get around the fact that you can't put the


    <model:LocationVO ...

    inside the UIComponent view class (like VBOX). I get a run time error when I try to do this. Or am I missing something?

  8. Derrick

    @phil, in Flex 4 you would put it between the fx:declarations in Flex 3 I have never had issues putting it anywhere within the mxml tags.

  9. Phil Herold

    I get an Error 1056: "Cannot create property..."; it's like it's trying to create property LocationVO on VBOX (as an example). So, it doesn't work for me. Flex 3.5, Flash Builder 4.


Leave a comment






wrap code blocks in <code> </code> tags