Javascript – Editing XML in Flex using e4x

apache-flexe4xjavascript

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?

Best Answer

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;
}