views:

9540

answers:

6

I know this is a fairly simple question, but I'm sure I'm not the first to have it, and won't be the last, so I thought it might be a good idea to get the answer in Stack Overflow for the benefit of everyone else, as well as not bugging my local Flex guru.

I've got an object that contains a dozen or so fields I want to bind to form elements, so that I can use that object to send the data back to the server to be saved.

Definition of my container object:

private static const emptyLink:Object = {
    id: -1, title:'',
    trigger1:'',trigger2:'',trigger3:'',trigger4:'',trigger5:'',
    linkTitle:'', linkBody:'',
    answer1:'',answer2:'',answer3:'',answer4:'',answer5:''
};

[Bindable] public var currentLink:Object = emptyLink;

currentLink is assigned at runtime to a specific index from an ArrayCollection, I'm just using the emptyLink object for initialization purposes, mostly.

<mx:Panel id="triggerPanel" title="Trigger" width="33%">
 <mx:VBox id="tpBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
  <mx:TextInput id="trigger1" width="100%" textAlign="left" text="{currentLink.trigger1}" />
  <mx:TextInput id="trigger2" width="100%" textAlign="left" text="{currentLink.trigger2}" />
  <mx:TextInput id="trigger3" width="100%" textAlign="left" text="{currentLink.trigger3}" />
  <mx:TextInput id="trigger4" width="100%" textAlign="left" text="{currentLink.trigger4}" />
  <mx:TextInput id="trigger5" width="100%" textAlign="left" text="{currentLink.trigger5}" />
 </mx:VBox>
</mx:Panel>

Of course, this compiles and displays just fine, but there are runtime warnings for each instance:

warning: unable to bind to property 'trigger1' on class 'Object' (class is not an IEventDispatcher) warning: unable to bind to property 'trigger2' on class 'Object' (class is not an IEventDispatcher) warning: unable to bind to property 'trigger3' on class 'Object' (class is not an IEventDispatcher) warning: unable to bind to property 'trigger4' on class 'Object' (class is not an IEventDispatcher) warning: unable to bind to property 'trigger5' on class 'Object' (class is not an IEventDispatcher)

And the currentLink object is not updated when the TextInput fields are changed.

The obvious answer is that my object needs to be an instance of a class that implements IEventDispatcher. What that answer doesn't tell me is the particulars of implementing that interface (what's required? what's not?), and if there is a simpler way to do this -- like a built in class that will gladly accept my custom properties and allow for binding, without me having to worry about the particulars of implementing the interface.

Does such a class exist? If not, what's the bare minimum and/or accepted standard for accomplishing this task?

A: 

I haven't been using Flex for very long, and this might not fit your requirements, but why not use XML? I believe you can set the TextInput text value to attributes in the XML.

I'm using pseudo-code, but something like this makes sense to me:

[Bindable] private static const currentLink:XML = <root>
                                                    <trigger1 value=""/>
                                                    <trigger2 value="" />
                                                  </root>;
...
<mx:TextInput id="trigger1" width ... text="{currentLink.trigger1.@value}" />

Something like this, perhaps?

bedwyr
+5  A: 

Object doesn't dispatch events. Although you have made the variable Bindable, the properties of the object referenced by the variable currentLink can not be bound.

Use ObjectProxy instead.

[Bindable] public var currentLink:ObjectProxy = new ObjectProxy(emptyLink);
Chetan Sastry
+1  A: 

Here's the livedocs reference for the interface. It's pretty much what would be obvious.

To quote:

In general, the easiest way for a user-defined class to gain event dispatching capabilities is to extend EventDispatcher.

Hence,

private static const emptyLink:EventDispatcher = {

le dorfier
your link doesn't go to the page within the livedocs that you thought it does. Each livedocs reference page has a "Current Link:..." in its footer, with the URL for directly-linking the page you're viewing. Use that. :)
Adam Tuttle
+4  A: 

The first thing you'll want to know is that binding in Flex 3 is not bidirectional. The binding expression will ensure that if the source of the binding expression (currentLink.trigger1) changes that the target (TextInput) will receive notification of the change and update accordingly. If you want the binding to go in the other direction, there are at least two ways to do this:

  1. Use the mx:Binding tag to direct TextInput.text back to the object
  2. Use BindingUtils to do this programmatically instead.

In Flex 4 they are introducing a new syntax for bidirectional binding @{some.binding.expression} but it's not available in Flex 3.

On the 2nd part: the error that you're receiving is because you are binding to a "generic" prototype Object. When you apply the [Bindable] metadata tag to a property or class, the MXMLC compiler generates AS code that includes use of binding utilities and property change watchers to do make the binding happen. However you can't make the prototype Object do this since it's a built-in. You can create a custom ActionScript class which is bindable (or has certain properties bindable). The MXMLC compiler will generate a class which implements IEventDispatcher and therefore supports binding. This has the advantage of being faster than prototype objects and also gives you compile-time checking, i.e. you will receive a compiler error if you reference an invalid property.

The other alternative is to wrap your prototype in ObjectProxy as one of the other SO members has suggested.

cliff.meyers
If I could accept 2 answers, this one would be #2. I picked Gabriel's because it was the simplest solution to do what I'm attempting to do. Thanks for all of the information, though!
Adam Tuttle
+4  A: 

You need to use ObjectProxy (as Chetan mentions) - but you also need to use valueCommit to get the text you enter in the input BACK into your object:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
        <![CDATA[
            import mx.utils.ObjectProxy;
              private static const emptyLink:Object = {
    id: -1, title:'',
    trigger1:'',trigger2:'',trigger3:'',trigger4:'',trigger5:'',
    linkTitle:'', linkBody:'',
    answer1:'',answer2:'',answer3:'',answer4:'',answer5:''
};

[Bindable] public var currentLink:ObjectProxy = new ObjectProxy(emptyLink);


private function handleClick():void
{
    trace(currentLink.trigger1);
}
]]>
</mx:Script>

<mx:Panel id="triggerPanel" title="Trigger" width="33%">
        <mx:VBox id="tpBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
                <mx:TextInput  id="trigger1" width="100%" textAlign="left" text="{currentLink.trigger1}" valueCommit="{currentLink.trigger1 = trigger1.text;}"/>

                <mx:Button label="Click" click="handleClick()"/>
        </mx:VBox>
</mx:Panel>        

</mx:WindowedApplication>
Gabriel
A: 

In general, the reason why you get "unable to bind to property foo on a class, is because you are either missing a getter or setter for foo. You could also make foo scoped to a public variable, (although this breaks encapsulation)

So you need Both of these to make it go away:

public function set foo (o:FooObject) : void { ... }

or

public function get foo() : FooObject { ... }

willedw