views:

2986

answers:

2

Hello,

In Flex, I have an xml document such as the following:

var xml:XML = <root><node>value1</node><node>value2</node><node>value3</node></root>

At runtime, I want to create a TextInput control for each node under root, and have the values bound to the values in the XML. As far as I can tell I can't use BindingUtils to bind to e4x nodes at runtime (please tell me if I'm wrong here!), so I'm trying to do this by hand:

for each (var node:XML in xml.node)
{
    var textInput:TextInput = new TextInput();
    var handler:Function = function(event:Event):void 
    {
        node.setChildren(event.target.text);
    };
    textInput.text = node.text();
    textInput.addEventListener(Event.CHANGE, handler);
    this.addChild(pileHeightEditor);
}

My problem is that when the user edits one of the TextInputs, the node getting assigned to is always the last one encountered in the for loop. I am used to this pattern from C#, where each time an anonymous function is created, a "snapshot" of the values of the used values is taken, so "node" would be different in each handler function.

How do I "take a snapshot" of the current value of node to use in the handler? Or should I be using a different pattern in Flex?

+2  A: 

Unfortunately, function closures work weird/poorly in Actionscript. Variables only get a "snapshot" when they go out of scope. Unfortunately, variables are function scoped, and not block scoped. So it doesn't end up working like you want.

You could create a dictionary to map from TextInput -> node, or you could stash the node in the TextInput's data property.

I wish what you described did work correctly since it is an easy/powerful way of expressing that.

Marc Hughes
+2  A: 

The closure only captures a reference to the variable, not its current value. Since local variables are Function-scoped (not block-scoped) each iteration through the loop creates a closure that captures a reference to the same variable.

You could extract the TextInput creation code into a separate function, which would give you a separate variable instance to capture for the closure. Something like this:

for each (var node:XML in xml.node)
{
    var textInput:TextInput = createTextInput(node);
    this.addChild(pileHeightEditor);
}
... 

private function createTextInput(node:XML) : TextInput {
    var textInput:TextInput = new TextInput();
    var handler:Function = function(event:Event):void 
    {
        node.setChildren(event.target.text);
    };
    textInput.text = node.text();
    textInput.addEventListener(Event.CHANGE, handler);
    return textInput;
}
bill d