Oct
20
Creating a two-way binding between Model and Form in Flex


Posted: 20th October 2008
Tags: , ,
Posted in Flex and Air
Comments: 14 Comments »

A quick sorry to all my Flex readership for the lack of Flex related posts these last few months. I though I would drop a quick tip / class I often use to create a two way binding between a form to a corresponding class model.

Flex provides a one way binding through its <mx:Binding> tags, if you are unfamiliar with this tag check out the section on them in Adobe’s Flex quick start guide. However if you want changes made to the model reflected in the form as well as changes made to the form reflected back in the model you need to set up two way bindings. To simplify this I have created a helper class (listed below) which then allows me to establish a two way binding.

package com.vibrant.components.Forms
{
	import flash.display.DisplayObject;
 
	import mx.binding.utils.BindingUtils;
	import mx.binding.utils.ChangeWatcher;
 
	public class ModelBinding
	{
 
		private var _modelWatcher		: ChangeWatcher;
		private var _componentWatcher		: ChangeWatcher;
		private var bindingsAreEstablished	: Boolean = false;
 
		public function ModelBinding()
		{
		}
 
		/**
		 * Property field
		 * @default null
		 */	
 
		private var _model : Object;
 
		public function set model( value : Object ):void
		{
			_model = value;
			updateBinding();
		}
 
		public function get model() : Object
		{
			return _model;
		}
 
		/**
		 * Property field, field within model to bind
		 * @default ""
		 */		
 
		private var _field:String;
 
		public function set field( value : String ):void
		{
			_field = value;
			updateBinding();
		}
 
		public function get field() : String
		{
			return _field;
		}
 
		/**
		 * Property: target, component to tartget binding from model to.
		 */		
 
		private var _target : Object;
 
		public function set target( value : Object ):void
		{
			_target = value;
			updateBinding();
		}
 
		public function get target() : Object
		{
			return _target;
		}
 
		/**
		 * Property: property, to bind to on target component
		 */		
 
		private var _property : String = "text";
 
		public function set property( value : String ):void
		{
			_property = value;
			updateBinding();
		}
 
		public function get property() : String
		{
			return _property;
		}
 
		/**
		 * @private
		 * Updates bindings between component and model
		 *
		 */
		private function updateBinding() : void
		{
			if ( bindingsAreEstablished ) clearBindings();
 
			if ( model != null &&  model.hasOwnProperty( field )
				&& target != null && target.hasOwnProperty( property ) )
			{
				_modelWatcher = BindingUtils.bindProperty( target, property, model, field );
				_componentWatcher = BindingUtils.bindProperty( model, field, target, property );
				bindingsAreEstablished = true;
			}
		}
 
		/**
		 * Clears bindings
		 *
		 */
		private function clearBindings() : void {
 
			if ( _modelWatcher != null )
			{
				_modelWatcher.unwatch();
				_modelWatcher = null;
			}
 
			if ( _componentWatcher != null )
			{
				_componentWatcher.unwatch();
				_componentWatcher = null;
			}
			bindingsAreEstablished = false;
		}
 
	}
}

This class basically takes a model class, that has the data you would like to setup the bindings with in. The field name, the name of the property within the model class to bind to. A target, the form component to establish the view side binding with and finally you can specify an optional property that the binding should be made to on the form element. If unspecified, the ‘property’ defaults to “text” as this is the most often used property to bind to. So for example given the following User model:

1
2
3
4
5
6
7
8
9
public class UserModel
{
    public var firstName : String;
    public var lastName : String;
 
	public function UserModel()
	{
	}
}

We could set up a binding to a TextInput as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
<mx:Script>
	<![CDATA[
		public var userModel : UserModel;
	]]>
</mx:Script>
<mx:Form>
	<mx:FormItem label="First Name">
		<mx:TextInput id="txtFirstName"/>
	</mx:FormItem>
</mx:Form>
<ModelBinding model="{userModel}" field="firstName" target="{txtFirstName}"/>
...

As you can see with this class you can quickly setup two-way bindings with your models.

Download ModelBinding.as
For more information on Binding, check out Adobe Devnet.

Comments below..

Advertisement

 




Comments

Note: You can leave a response, or trackback from your own site.

 
Comment:

nice, I was just looking for something like this.

 
 
Comment:

Thanks for that helpful class! Added to my list of Flex links :-)

 
 
Comment:

Hey Jon,

nice stuff – you’ve just reminded me that I’ve got some spike’s laying around that expand on these ideas too – I’ll blog them up later this week.

 
 
Comment:

Nice, I need some think like this. Any change to dynamic link a model with a form in flx?

 
 
Comment:

funciona!!!!!! Muchisimas gracias!!!!!!!!!!!

 
 
Comment:

What for Binding a model.field to a Combobox ?

 
 
Comment:

we solved the problem with combos!

 
 
Comment:

Very nice class.. thanks..

 
 


Jamie Jackson
14/01/2009
5:36 pm

Comment:

This doesn’t seem to work with mx:DateField out of the box. http://pastebin.com/f58b27b7d
http://pastebin.com/f53acddbf

 
 


Jamie Jackson
14/01/2009
6:33 pm

Comment:

Never mind, got it. You have to add the “property” attribute, e.g.,

 
 


Jamie Jackson
14/01/2009
6:34 pm

Comment:

(Trying that again, with escaped HTML.)

<form:ModelBinding
model=”{conference}”
field=”materialsDueDate”
target=”{conferenceForm_materialsDueDate}”
property=”selectedDate”
/>

 
 


yakafokon
19/01/2009
5:02 pm

Comment:

Very usefull ! thanx for sharing !
It seems that two way binding is a common issue, I’ve found this on the net :
http://bugs.adobe.com/jira/browse/SDK-11193
and the adobe solution (available in flex 4 ???)
http://opensource.adobe.com/wiki/display/flexsdk/Two-way+Data+Binding

To come back to your solution, in my opinion the main drawback is that we are loosing the error warning during compilation time :
For example if I introduce a wrong field name I’ll not be warned :

But with this, even if it takes 2 lines, I’ll be warned during compile time

Another issue with binding is the type conversion, for example between an int type of your object model and the String type of the TextInput : how do you handle this issue, do you have any tips ?

Thanx

 
 
Pingback:

[...] Another solution for creating a two way binding using a “in-house utility api” found here [...]

 
 


TJ Downes
17/05/2009
4:52 pm

Comment:

Hey Jon, thanks for this excellent shortcut for two way binding. I’ve actually gone and replaced a significant amount of code with it, which has made my projects more readable

I did have questions on the dates and combobx binding. Others seem to have it working, but mine are flailing. When bound to dateFields the binding seems to be one-way still, when bound to the selectedDate property.

Which comboboxes It doesnt seem to work at all unless I am using and array of strings as the dataProvider. If I have an ArrayCollection or Array of objects nothing seems to allow it to bind. Any suggestions?

Also, this component doesn’t seem to support binding of properties of nested objects, for example user.pet.petName to petName.text.

Lastly, I noticed that you have a grassroots project going on, stubmatic. I am working on a project that I believe would be complimentary to yours, we should talk :D

TJ

 
Add a comment:

 


View Jon Baker's profile on LinkedIn


View blog authority