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.
Derrick Grigg is a Rich Internet Application (RIA) freelance contractor based in Toronto, Canada. He specializes in architecting and developing applications using a variety of technologies, most notably Flash, Flex and Coldfusion.
Oct 23, 2009 at 10:37 AM
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.
Oct 23, 2009 at 12:41 PM
<pre><mx:TextInput id="zipTxt" text="{location.zip}" valueCommit="{location.zip = zipTxt.text}" /></pre>
Last try I swear!
Oct 25, 2009 at 12:28 AM
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.
Nov 05, 2009 at 8:54 PM
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?
Nov 06, 2009 at 4:47 PM
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.
May 27, 2010 at 8:54 AM
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!
May 29, 2010 at 10:25 AM
So how do you get around the fact that you can't put the
inside the UIComponent view class (like VBOX). I get a run time error when I try to do this. Or am I missing something?
May 29, 2010 at 6:15 PM
@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.
May 31, 2010 at 8:58 AM
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.