Flex: understanding databinding: oneway and twoway

17 02 2009

Something that isn’t that well talked about in the Flex world is two way databinding, and it’s implications. 

I was working with an MVC project recently and I came up to an interesting problem with an ArrayCollection somewhere in the View that was bound oneway from the Model. When I changed a property of the object in the View, it changed in the Model. Huh? I didn’t use two-way databinding so why would this happen?

I realised this was due to the fact that while one-way databinding doesn’t change the source when the destination object changes, it DOES ensure that both objects have the same reference. The implication here is that if you change a property of the destination object, it will change in the source as well. They are the same object reference after all.

Let’s prove this with examples. Simple databinding in Flex is trivial. 

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" backgroundColor="white">

<mx:Script>
<![CDATA[

[Bindable]
public var someName:String;

]]>
</mx:Script>

<mx:TextInput text="{someName}" />

<mx:Label text="This is the local obj 'someName': {someName}" />

</mx:Application>

That’s all well and good but what if we want to change the someName variable back? We insert a Binding tag to bind back – two way binding.

<mx:Binding source="textInput.text" destination="someName" />

OK great. But something is happening here that needs to be discussed.  The binding is REPLACING the reference of “someName” to that of a new String. So it may be logical to assume that we need two-way binding when updating the destination of some binding to reflect the changes in the source. But, hang on:

Say I have this Model: 

package
{

	[Bindable]
	public class TestModel
	{
		public static var obj:TestObject = new TestObject("test via model","me via model");
	}

}

And say I have some Object: (TestObject.as)

package
{
	[Bindable]
	public class TestObject
	{

		public var name:String;

		public var desc:String;

		public function TestObject(n:String, d:String)
		{
			this.name = n;
			this.desc = d;
		}

	}
}

And this is my Application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" backgroundColor="white">

	<mx:Script>
		<![CDATA[

			[Bindable]
			public var someObj:TestObject;

		]]>
	</mx:Script>

	<mx:Binding source="TestModel.obj" destination="this.someObj" />

	<mx:Binding source="textInput.text" destination="someObj.name" />

	<mx:Label id="someLabel" text="From the MODEL: {TestModel.obj.name}" />

	<mx:TextInput id="textInput"  />

	<mx:Label text="This is the name locally: {someObj.name}" />

</mx:Application>

I’m simply binding the Model to the local “someObj” (one-way) and then ensuring that changing the TextInput will change the name property of “someObj”. What happens? Try it. 

testbinding

Notice how my Model is also changing? So, you can see that one-way binding actually ensures that both the source and the destination point to the same object reference. You use two-way binding if you need to change the entire destination object completely and want this to be reflected in the source.

Let’s have a look at the final example:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" backgroundColor="white">

	<mx:Script>
		<![CDATA[

			[Bindable]
			public var someObj:TestObject;

			private function onEnter():void
			{
				someObj = new TestObject("created locally","local");
			}

		]]>
	</mx:Script>

	<mx:Binding source="TestModel.obj" destination="this.someObj" />

	<mx:Binding source="textInput.text" destination="someObj.name" />

	<mx:Label id="someLabel" text="From the MODEL: {TestModel.obj.name}" />

	<mx:TextInput id="textInput" enter="onEnter()"  />

	<mx:Label text="This is the name locally: {someObj.name}" />

</mx:Application>

Now, when you hit “ENTER” on your keyboard aftering entering some text, you’ll see the local object now points to a new object instance. And, you can see the Model doesn’t change from this point on, because it still points to the original instance. If you wanted the Model to update from this change, you would add:

<mx:Binding source="someObj" destination="TestModel.obj" />

 

Although you’d be careful to insert it AFTER the Binding tag that assigns someObj as the destination and TestModel as the source. Otherwise you’ll set the Model to null before you begin!

Make sense?