Creating a two-way binding between Model and Form in Flex
20 October 2008, 23:56, by JonA 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.
14 comments below:
Thanks for that helpful class! Added to my list of Flex links
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 by George — 26 October 2008 @ 22:33Nice, I need some think like this. Any change to dynamic link a model with a form in flx?
Comment by Carro Brasilia — 9 November 2008 @ 20:57This doesn’t seem to work with mx:DateField out of the box. http://pastebin.com/f58b27b7d
http://pastebin.com/f53acddbf
Never mind, got it. You have to add the “property” attribute, e.g.,
Comment by Jamie Jackson — 14 January 2009 @ 18:33(Trying that again, with escaped HTML.)
<form:ModelBinding
model=”{conference}”
field=”materialsDueDate”
target=”{conferenceForm_materialsDueDate}”
property=”selectedDate”
/>
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
Comment by yakafokon — 19 January 2009 @ 17:02[...] Another solution for creating a two way binding using a “in-house utility api” found here [...]
Pingback by Two way binding with Flex « Yakafokon’s Blog — 28 January 2009 @ 10:11Hey 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
TJ
Comment by TJ Downes — 17 May 2009 @ 16:52