views:

1543

answers:

3

Hi, I have a custom actionscript object defined as bindable with a number of public properties.

[Bindable]
public class MyObject extends Object {   

    public var mobileNumber:String;
...

In my mxml I have:

<mx:Script><![CDATA[
    import mx.binding.utils.BindingUtils;
    import org.test.MyObject;

    [Bindable]
    private var obj: MyObject = new MyObject();
]]></mx:Script>

<mx:Label text="Mobile Number" id="mobileNumberLabel"/>
<mx:TextInput id="mobileNumberText" text="{obj.mobileNumber}" />

<mx:LinkButton label="Load" id="loadButton" enabled="true" click="obj = obj.load();"/>
<mx:LinkButton label="Save" id="saveButton" enabled="true" click="obj.write();"/>

My issue is that when I enter a new value in the field for mobile number and then click the save button, the value typed is not logged out... i.e.:

    public function write():void {
     var bytes:ByteArray = new ByteArray();
     trace("write - mobile:" + this.mobileNumber);

     bytes.writeObject(this);
     EncryptedLocalStore.setItem(KEY, bytes);
    }

I also tried adding in:

    private function init():void {
        BindingUtils.bindProperty(mobileNumberText, "text", obj, "mobileNumber");
    }

but no luck with that either.

I'm probably missing something simple here, but not sure what it is. Hope you can help, thanks.

A: 

uhm, did you try implementing IEventDispatcher, or extending EventDispatcher? bindings work through dispatching of PropertyChangeEvents, so maybe this is the problem? also, i am not sure, whether bindings work on variables, or whether they require properties (getters/setters) ... you should try compiling with -keep-generated-actionscript and see if the resulting code makes any sense, i.e. dispatches any events, if the variable is accessed ...

greetz back2dos

back2dos
+1  A: 

This code:

<mx:TextInput id="mobileNumberText" text="{obj.mobileNumber}" />

makes a one-directional binding between obj.mobileNumber and mobileNumberText.text. What you need is an additional reverse binding - add the following line to your code and it will work:

<mx:Binding source="mobileNumberText.text" destination="obj.mobileNumber"/>
tst
+5  A: 

tst's answer is correct - bindings are one-way. I'm guessing you already knew that though, since you tried to setup the reverse binding in your init() method.

However, there's two problems with your init() method.

First, it's not clear where you put that init() method, or what calls it.

Second, you got the method parameters backwards.

What I typically do in situations like this is either use the mxml tag as the first responder suggested, or if I'm in AS3 code, I do something like this:

private function onCreationComplete(event:Event):void
{
  BindingUtils.bindProperty(obj, "mobileNumber", mobileNumberText, ["text"]);
}

Note a couple of points here:

1/ BindingUtils.bindProperty() is "left hand side = right hand side". Thus, it's kinda like writing

  obj.mobileNumber = mobileNumberText.text;

Or, closer to what is "actually going on" inside the binding classes:

  obj["mobileNumber"] = mobileNumberText["text"];

2/ BindingUtils.bindProperty() actually wants an array as the last param. This is so that you can do "chained properties" logically like:

obj.mobileNumber = mobileNumbersGrid.selectedItem.text;

which would be

BindingUtils.bindProperty(obj, "mobileNumber", mobileNumbersGrid,
    ["selectedItem", "text"]);

3/ Best practice tip: if you're binding a property chain whose initial member is a property of yourself (this), then write it as:

BindingUtils.bindProperty(obj, "mobileNumber", this,
    ["mobileNumbersGrid", "selectedItem", "text"]);

This neatly handles the case where your "right hand side object" this.mobileNumbersGrid instance itself is replaced with a new instance object.

4/ If you ever reassign obj ("the left hand side"), you need to create a new binding to the new obj instance. Typically, you do this by turning the local obj property into a getter/setter pair like this:

  public function get obj():MyObject
  {
    return _obj;
  }

  private var _obj:MyObject = new MyObject();

  public function set obj(value:MyObject):void
  {
    _obj = value;
    BindingUtils.bindProperty(_obj, "mobileNumber", mobileNumberText, ["text"]);
  }

(Note: to be really careful, you'd stash the returned value from BindingUtils.bindProperty() which is a ChangeWatcher instance, and you'd unwatch() to prevent the old object from receiving property changes)

Hope this helps. Bindings are among the most powerful tools in the Flex framework, but can cause a lot of headaches when used poorly. In particular, be aware of memory leaks and "unexpected updates" when bindings are left "hanging around".

verveguy
Kieren H - does this solve your problem? Please mark as answered if so!
verveguy
yup, ta muchly.
Kieran H
You are a total legend verve - Ive been having so many binding issues, and your answer here has explained a lot of whats going on, and why things seem to randomly not work at times. GW
dalyons
I should have elaborated on the memory leak implications of bindings as well. To observe the changes in the "right hand side", the binding machinery adds an event listener to the right hand side objects - each of the objects in the whole chain. These event listeners point back at the *binding objects* (ChangeWatchers) and thus, will keep those watchers in memory even after your own object has gone away. For this reason, I try to always keep a ref to the watcher (watcher = BindingUtils.bindProperty(...)) and then watcher.unwatch() when my object is going away to remove all these event listeners
verveguy